I want to make sure my code meet the standard and security - mysql

Am new to ASP.NET and i just developed a simple online examination portal for learning.
I used ADO.NET,MySql and developed in VS 2010.
I have a login page in which user can login and register page for new user.After successful login user is redirected to the question page and i fetch the first question from database.
I populated the question in label and options in Radio Button list.User can select one option and click next button.
In the click event of next button i calculate the marks.
I store all values in session only.When user click next of last question that is 4 user is redirected to result page and print the marks.
This is my code
public partial class Questions : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
Response.Cache.SetNoStore();
if (!IsPostBack)
{
renderQuestions(1);
Session["buttonIndex"] = 1;
Session["Marks"] = 0;
}
}
public void renderQuestions(int index)
{
MySqlConnection con = null;
string conString = ConfigurationManager.ConnectionStrings["conString"].ConnectionString;
string qry = "SELECT * FROM QUESTIONS WHERE QUESTION_ID="+index+"";
try
{
using (con = new MySqlConnection(conString))
{
con.Open();
using (MySqlCommand cmd = new MySqlCommand(qry, con))
{
using (MySqlDataAdapter ada = new MySqlDataAdapter(cmd))
{
DataTable dt = new DataTable();
ada.Fill(dt);
if (dt.Rows.Count > 0)
{
clsQuestion ques = new clsQuestion();
ques.QuestionId = Convert.ToInt32(dt.Rows[0][0]);
ques.Question = Convert.ToString(dt.Rows[0][1]);
ques.Option1 = Convert.ToString(dt.Rows[0][2]);
ques.Option2 = Convert.ToString(dt.Rows[0][3]);
ques.Option3 = Convert.ToString(dt.Rows[0][4]);
ques.Option4 = Convert.ToString(dt.Rows[0][5]);
ques.Answer = Convert.ToInt32(dt.Rows[0][6]);
renderQuesAndAnswers(ques);
}
}
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
}
}
public void renderQuesAndAnswers(clsQuestion quest)
{
lblQuestion.Text = quest.Question;
RadioButtonList1.Items.Clear();
RadioButtonList1.Items.Add(quest.Option1);
RadioButtonList1.Items.Add(quest.Option2);
RadioButtonList1.Items.Add(quest.Option3);
RadioButtonList1.Items.Add(quest.Option4);
Session["QuestionNumber"] = quest.QuestionId ;
Session["Answer"] = quest.Answer;
}
public class clsQuestion
{
private int questionId;
private string question;
private string option1;
private string option2;
private string option3;
private string option4;
private int answer;
public int QuestionId
{
get { return questionId; }
set { questionId = value; }
}
public string Question
{
get { return question; }
set { question = value; }
}
public string Option1
{
get { return option1; }
set { option1 = value; }
}
public string Option2
{
get { return option2; }
set { option2 = value; }
}
public string Option3
{
get { return option3; }
set { option3 = value; }
}
public string Option4
{
get { return option4; }
set { option4 = value; }
}
public int Answer
{
get { return answer; }
set { answer = value; }
}
}
protected void option1_CheckedChanged(object sender, EventArgs e)
{
if (Convert.ToInt32 (Session["Answer"]) == 1)
{
int marks=Convert.ToInt32 (Session["Marks"]);
marks++;
Session["Marks"] = marks;
}
}
protected void option2_CheckedChanged(object sender, EventArgs e)
{
if (Convert.ToInt32(Session["Answer"]) == 2)
{
int marks = Convert.ToInt32(Session["Marks"]);
marks++;
Session["Marks"] = marks;
}
}
protected void option3_CheckedChanged(object sender, EventArgs e)
{
if (Convert.ToInt32(Session["Answer"]) == 3)
{
int marks = Convert.ToInt32(Session["Marks"]);
marks++;
Session["Marks"] = marks;
}
}
protected void option4_CheckedChanged(object sender, EventArgs e)
{
if (Convert.ToInt32(Session["Answer"]) == 4)
{
}
}
protected void btnNext_Click(object sender, EventArgs e)
{
}
protected void btnNext_Click1(object sender, EventArgs e)
{
int buton = Convert.ToInt32(Session["buttonIndex"]);
if (buton < 5)
{
if (RadioButtonList1.SelectedIndex + 1 == Convert.ToInt32(Session["Answer"]))
{
int marks = Convert.ToInt32(Session["Marks"]);
marks++;
Session["Marks"] = marks;
}
Session["buttonIndex"] = Convert.ToInt32(Session["buttonIndex"]) + 1;
renderQuestions(Convert.ToInt32(Session["buttonIndex"]));
if (buton == 4)
{
Server.Transfer("Results.aspx");
Session.RemoveAll();
}
}
}
}
this is my HTML
<form id="form1" runat="server">
<div>
<h3>Please choose the right answer</h3>
</div>
<table class="style1">
<tr>
<td class="style3">
<asp:Panel ID="Panel1" runat="server">
<asp:Label ID="lblQuestion" runat="server" Text=""></asp:Label>
</asp:Panel>
</td>
<td class="style4">
</td>
</tr>
<tr>
<td class="style2">
Answers:</td>
<td>
</td>
</tr>
<tr>
<td class="style2">
<asp:Panel ID="Panel2" runat="server">
<asp:RadioButtonList ID="RadioButtonList1" runat="server">
</asp:RadioButtonList>
<asp:RadioButton ID="option1" runat="server" Checked="false" AutoPostBack="True"
GroupName="Option" oncheckedchanged="option1_CheckedChanged" />
<asp:RadioButton ID="option2" runat="server" Checked="false" AutoPostBack="True"
GroupName="Option" oncheckedchanged="option2_CheckedChanged" />
<asp:RadioButton ID="option3" runat="server" Checked="false" AutoPostBack="True"
GroupName="Option" oncheckedchanged="option3_CheckedChanged" />
<asp:RadioButton ID="option4" runat="server" Checked="false" AutoPostBack="True"
GroupName="Option" oncheckedchanged="option4_CheckedChanged" />
</asp:Panel>
</td>
<td>
</td>
</tr>
<tr>
<td class="style2">
</td>
<td>
</td>
</tr>
<tr>
<td class="style2">
<asp:Button ID="btnNext" runat="server" onclick="btnNext_Click1" Text="Next" />
</td>
<td>
</td>
</tr>
</table>
</form>
I got the result perfect and no issues yet.But i want to make sure am doing the best way and do my code meet the standards and is there any security issues.
Please some one guide me through this if u can.Thanks in advance.

Related

PaginatedList onpostasync - ArgumentNullException: Value cannot be null. (Parameter 'model') when submitting on paginnatelist

HI Perhaps someone can point me in the right direction.
I created a paginated list page use the example on the ms site.
https://learn.microsoft.com/en-us/aspnet/core/data/ef-rp/sort-filter-page?view=aspnetcore-5.0
and modified it slightly. I replaced one item with a input box as would like to edit the list and do a bulk save instead on opening every item on a new page to edit it.
but when it click the submit button i get the error.
ArgumentNullException: Value cannot be null. (Parameter 'model')
Microsoft.AspNetCore.Mvc.RazorPages.PageModel.TryValidateModel(object model, string name)
if I bind the property
[BindProperty(SupportsGet = true)]
public PaginatedList<CompanyDataListing> CustomersDisplayList { get; set; }
i get the follow error
ArgumentException: Type 'BizFinder.PaginatedList`1[BizFinder.Data.CompanyDataListing]' does not have a default constructor (Parameter 'type')
and even the paginated list does not render.
My code for the sumbit is as follows.
public PaginatedList<CompanyDataListing> CustomersDisplayList { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!TryValidateModel(CustomersDisplayList, nameof(CustomersDisplayList)))
{
return Page();
}
foreach (var item in CustomersDisplayList)
{
if (item.GoogleCategory != "")
{
string cat = item.GoogleCategory;
}
}
return Page();
}
and my html is as follow.
#using (Html.BeginForm(FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<table class="table">
<thead>
<tr>
<th>
<a asp-page="./Index" asp-route-sortOrder="#Model.NameSort"
asp-route-currentFilter="#Model.CurrentFilter">
#Html.DisplayNameFor(model => model.CustomersDisplayList[0].CompanyName)
</a>
</th>
<th>
#Html.DisplayNameFor(model => model.CustomersDisplayList[0].Keywords)
</th>
<th>
<a asp-page="./Index" asp-route-sortOrder="#Model.DateSort"
asp-route-currentFilter="#Model.CurrentFilter">
#Html.DisplayNameFor(model => model.CustomersDisplayList[0].GoogleCategory)
</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.CustomersDisplayList)
{
<tr>
<td>
<input type="hidden" asp-for="CustomersDisplayList[0].Id" />
#Html.DisplayFor(modelItem => item.CompanyName)
</td>
<td>
#Html.DisplayFor(modelItem => item.Keywords)
</td>
<td>
#*#Html.DisplayFor(modelItem => item.GoogleCategory)*#
<input asp-for="CustomersDisplayList[0].GoogleCategory" name="Category1" placeholder="Input your keyword" class="form-control GoogleCategory" autofocus="autofocus" />
<span asp-validation-for="CustomersDisplayList[0].GoogleCategory" class="text-danger"></span>
</td>
<td>
<a asp-page="./Edit" asp-route-id="#item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="#item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="#item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
<div asp-validation-summary="All">
<span>Please correct the following errors</span>
</div>
#* #Html.ValidationSummary(true, "", new { #class = "text-danger" })*#
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
}
and the full code base is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BizFinder.Data;
using BizFinder.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
//https://learn.microsoft.com/en-us/aspnet/core/data/ef-rp/sort-filter-page?view=aspnetcore-5.0
namespace BizFinder.Pages.Admin
{
public class ManCatergoryModel : PageModel
{
private readonly ApplicationDbContext _context;
private readonly IConfiguration Configuration;
private readonly IAuthorizationService _authorizationService;
public int PageSize { get; set; } = 10;
// private readonly SchoolContext _context;
public ManCatergoryModel(ApplicationDbContext context, IConfiguration configuration)
{
_context = context;
Configuration = configuration;
}
public string NameSort { get; set; }
public string DateSort { get; set; }
public string CurrentFilter { get; set; }
public string CurrentSort { get; set; }
// public IList<CompanyDataListing> CustomersDisplayList { get; set; }
// [BindProperty(SupportsGet = true)]
public PaginatedList<CompanyDataListing> CustomersDisplayList { get; set; }
public async Task OnGetAsync(string sortOrder,
string currentFilter, string searchString, int? pageIndex)
{
CurrentSort = sortOrder;
NameSort = String.IsNullOrEmpty(sortOrder) ? "CompanyName" : "";
DateSort = sortOrder == "Keywords" ? "date_desc" : "Date";
if (searchString != null)
{
pageIndex = 1;
}
else
{
searchString = currentFilter;
}
CurrentFilter = searchString;
// using System;
IQueryable<CompanyDataListing> CompData = from s in _context.CompanyDataList
where s.GoogleCategory == null
select s;
switch (sortOrder)
{
case "name_desc":
CompData = CompData.OrderByDescending(s => s.CompanyName);
break;
case "Date":
CompData = CompData.OrderBy(s => s.Keywords);
break;
case "GoogleCategory":
CompData = CompData.OrderByDescending(s => s.GoogleCategory);
break;
default:
CompData = CompData.OrderBy(s => s.CompanyName);
break;
}
// var pageSize = Configuration.GetValue("PageSize", 4);
CustomersDisplayList = await PaginatedList<CompanyDataListing>.CreateAsync(
CompData.AsNoTracking(), pageIndex ?? 1, PageSize);
}
public async Task<IActionResult> OnPostAsync(PaginatedList<CompanyDataListing> Datalist)
{
// List < CompanyDataListing > mm = CustomersDisplayList.ToList();
foreach (var item in Datalist)
{
string str = item.GoogleCategory;
}
return Page();
}
}
}
The first thing you need to know is that the pagination in the tutorial is the back-end pagination. When you click the next page, the data is re-requested every time, so if you want to submit in batches, you can only submit current page data.
If you want the data of the current page to be submitted in batches, then you can modify your code as follows.
PageModel:
public class IndexModel : PageModel
{
private readonly PageListRazorContext _context;
private readonly IConfiguration Configuration;
public IndexModel(PageListRazorContext context, IConfiguration configuration)
{
_context = context;
Configuration = configuration;
}
public string NameSort { get; set; }
public string KeywordsSort { get; set; }
public string CurrentSort { get; set; }
public PaginatedList<CompanyDataListing> CompanyDataListing { get;set; }
public async Task OnGetAsync(string sortOrder,int? pageIndex)
{
NameSort = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
KeywordsSort = sortOrder == "Keywords" ? "keywords_desc" : "Keywords";
IQueryable<CompanyDataListing> company = from s in _context.CompanyDataListing
select s;
switch (sortOrder)
{
case "name_desc":
company = company.OrderByDescending(s => s.CompanyName);
break;
case "Keywords":
company = company.OrderBy(s => s.Keywords);
break;
case "keywords_desc":
company = company.OrderByDescending(s => s.Keywords);
break;
default:
company = company.OrderBy(s => s.CompanyName);
break;
}
var pageSize = Configuration.GetValue("PageSize", 4);
CompanyDataListing = await PaginatedList<CompanyDataListing>.CreateAsync(
company.AsNoTracking(), pageIndex ?? 1, pageSize);
}
public IActionResult OnPost(List<CompanyDataListing> Datalist)
{
//....
return RedirectToPage("./Index");
}
}
Page:
<form method="post">
<table class="table">
<thead>
<tr>
<th>
<a asp-page="./Index" asp-route-sortOrder="#Model.NameSort">
#Html.DisplayNameFor(model => model.CompanyDataListing[0].CompanyName)
</a>
</th>
<th>
<a asp-page="./Index" asp-route-sortOrder="#Model.KeywordsSort">
#Html.DisplayNameFor(model => model.CompanyDataListing[0].Keywords)
</a>
</th>
<th>
#Html.DisplayNameFor(model => model.CompanyDataListing[0].GoogleCategory)
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.CompanyDataListing)
{
<tr>
<td>
<input type="hidden" asp-for="#item.CompanyName" name="Datalist[#i].CompanyName" />
#Html.DisplayFor(modelItem => item.CompanyName)
</td>
<td>
<input type="hidden" asp-for="#item.Keywords" name="Datalist[#i].Keywords" />
#Html.DisplayFor(modelItem => item.Keywords)
</td>
<td>
<input asp-for="#item.GoogleCategory" name="Datalist[#i].GoogleCategory" placeholder="Input your keyword" class="form-control GoogleCategory" autofocus="autofocus" />
<span asp-validation-for="#item.GoogleCategory" class="text-danger"></span>
</td>
<td>
<a asp-page="./Edit" asp-route-id="#item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="#item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="#item.ID">Delete</a>
</td>
#{i++;}
</tr>
}
</tbody>
</table>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
#{
var prevDisabled = !Model.CompanyDataListing.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.CompanyDataListing.HasNextPage ? "disabled" : "";
}
<a asp-page="./Index"
asp-route-sortOrder="#Model.CurrentSort"
asp-route-pageIndex="#(Model.CompanyDataListing.PageIndex - 1)"
class="btn btn-primary #prevDisabled">
Previous
</a>
<a asp-page="./Index"
asp-route-sortOrder="#Model.CurrentSort"
asp-route-pageIndex="#(Model.CompanyDataListing.PageIndex + 1)"
class="btn btn-primary #nextDisabled">
Next
</a>
</form>
Test result:
You are referencing CustomerDisplayList in the OnPost handler but it has not been initialised, hence the exception. You initialised it in the OnGet handler, but that only executes for GET requests. If you want to use a POST request, you also need to initialise the CustomerDisplayList in that too.
For what it's worth, the example you linked to uses GET requests for the whole cycle. This usually makes more sense for things like paginating and filtering results. That way, the criteria are passed in the URL as query string values or route data, which means that you can bookmark filtered/paged results. You won't need to render the anti forgery token for GET requests.

Changing HTML with Java(Spring)

I am a newbie at Spring and I am writing a rental system for movies. I have a Controller where I can get a list of all movies which are still rented (marked by digit "1" as a status in my Database) and which are already have been returned(marked as "0").
Now currently, when I call the page I see all the rented movies with the current status "1" or "0" as well as already returned movies which can still be returned and have a checkbox (which shouldn't be possible.
My question is, how can I change the HTML in the following way:
The status "1" or "0" changes to "rented" and "returned"
I want to remove the checkbox on all movies which already have been returned.
My code:
#Controller
public class MovieController {
#Autowired
private MovieService movieService;
#GetMapping("/home")
public String home(Model model) {
return "index";
}
#GetMapping("/movieList")
public String listAllMovies(Model model) {
model.addAttribute("listMovies", movieService.findNotRented());
return "movies";
}
#GetMapping("/search")
public String findByOption(Model model, #RequestParam(value = "search") String search,
#RequestParam("options") String options) {
if (options.equals("movie")) {
model.addAttribute("listMovies", movieService.findByName(search));
} else if (options.equals("actor")) {
model.addAttribute("listMovies", movieService.findMovieByActor(search));
} else if (options.equals("genre")) {
model.addAttribute("listMovies", movieService.findMovieByGenre(search));
}
return "movies";
}
#GetMapping("/rentedList")
public String findRentedMovies(Model model) {
model.addAttribute("listMovies", movieService.findRentedMovies());
return "rented";
}
#GetMapping("/rentMovie")
public String rentMovie(Model model, #RequestParam int id) {
model.addAttribute("listMovies", movieService.rentMovie(id));
return "index";
}
#GetMapping("/deleteRentedMovie")
public String deleterentedMovie(Model model, #RequestParam int id) {
model.addAttribute("listMovies", movieService.deleteRentedMovie(id));
return "index";
}
#GetMapping("/rentMovie2")
public String rentMovie2(Model model, #RequestParam("idChecked") List<Integer> id) {
if (id != null) {
for (Integer idInt : id) {
model.addAttribute("listMovies", movieService.rentMovie(idInt));
}
}
return "index";
}
#GetMapping("/deleteRentedMovie2")
public String deleterentedMovie(Model model, #RequestParam("idChecked") List<Integer> id) {
if (id != null) {
for (Integer idInt : id) {
model.addAttribute("listMovies", movieService.deleteRentedMovie(idInt));
}
}
return "index";
}
}
Thymeleaf
<h1>Movie List</h1>
<form action="/deleteRentedMovie2">
<table>
<tr>
<th>Title</th>
<th>Time rented</th>
<th>Status</th>
<th>Select</th>
</tr>
<tr th:each="movie : ${listMovies}">
<td th:text="${movie.title}"></td>
<td th:text="${movie.date}"></td>
<td th:text="${movie.status}"></td>
<td><input type="checkbox" th:name="idChecked" th:value="${movie.id}"></td>
</tr>
</table>
<input type="submit" value="Return Movie">
</form>
Thank you in advance and sorry for my bad English
For the status I would something like:
<td>
<span th:if="${movie.status == 1}">rented</span>
<span th:if="${movie.status == 0}">returned</span>
</td>
You could also use the Elvis operator but it is maybe less readable.
For the checkbox:
<td><input th:unless="${movie.status == 0}" type="checkbox" th:name="idChecked" th:value="${movie.id}"></td>

ASP.NET runtime error:code blocks are not supported in this context-with ListView

i have this listView and textBox:
<table>
<tr><td>Reciver:<table><tr>
<asp:ListView ID="showRecivers" runat="server"><td><%# Eval("name")%></td> </asp:ListView>
</tr></table>
<asp:TextBox ID="reciver" runat="server" OnTextChanged="style_Recivers" AutoPostBack="true"></asp:TextBox>
</td></tr></table>
the list the listview is bound to:
public List<Reciver> recivers = new List<Reciver>();
and the function style_Recivers:
protected void style_Recivers(object sender, EventArgs e)
{
string[] separator = new string[] { "," };
string[] reciversArray = reciver.Text.ToString().Split(separator, StringSplitOptions.None);
reciversArray = reciversArray.Distinct().ToArray();
for (int i = 0; i < reciversArray.Length; i++)
{
recivers.Add(new Reciver(reciversArray[i]));
}
this.showRecivers.DataSource = recivers;
this.showRecivers.DataBind();
}
and class Reciver:
public class Reciver
{
public string name;
public Reciver(string name)
{
this.name = name;
}
public string getName()
{
return this.name;
}
public void setName(string name)
{
this.name = name;
}
}
what my idea is, that when a couple of names eneted to the textBox with a , saperator, the style_Reciver function is activated and each name is shown in the ListView right away.
but it doesnt work, it gives me the error
ASP.NET runtime error:code blocks are not supported in this context
and marks this line:
<asp:ListView ID="showRecivers" runat="server"><td><%# Eval("name")%></td> </asp:ListView>
for starter. probably more thing wont work but this is the first thing.
how can i fix it? Thanks for the help
EDIT:
it works after i added <ItemTemplate>
now it gives me a different bug:
Reciver' does not contain a property with the name 'name'
whhat is the problem now?
The List View content here should be wrapped into ItemTemplate:
<asp:ListView ID="showRecivers" runat="server">
<ItemTemplate>
<td><%# Eval("name")%></td>
</ItemTemplate>
</asp:ListView>
Update. Also there is a problem with your class declaration. Here is how it should be declared in C# conventional way:
public class Reciver
{
public string _name;
public Reciver(string name)
{
this.name = name;
}
public string name
{
get { return this._name; }
set { this._name = value; }
}
}

How can I use ext:CheckColumn in gridpanel with Linq2Sql?

I just want to bind the data in a gridpanel which uses CheckColumn. The checkboxes need to be checked if 'true' and unchecked if 'false'. I don't know how to do this. I need your help. Please I need a simple example.
U dont need to do any special thing.just decribe the colum as a CheckColumn and bind data ,here is the simple example
<ext:GridPanel runat="server" ID="grd" Height="300" Layout="FitLayout">
<Store>
<ext:Store runat="server">
<Model>
<ext:Model runat="server">
<Fields>
<ext:ModelField Name="data0"></ext:ModelField>
<ext:ModelField Name="data1"></ext:ModelField>
<ext:ModelField Name="data2"></ext:ModelField>
</Fields>
</ext:Model>
</Model>
</ext:Store>
</Store>
<ColumnModel>
<Columns>
<ext:Column ID="Column1" runat="server" DataIndex="data0" Text="data0"></ext:Column>
<ext:Column ID="Column2" runat="server" DataIndex="data1" Text="data1"></ext:Column>
<ext:CheckColumn runat="server" DataIndex="data2" Text="data2"></ext:CheckColumn>
</Columns>
</ColumnModel>
</ext:GridPanel>
and code behind;
public partial class TestCase : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//grd.GetStore().DataSource = GetData();
//grd.DataBind(); //OR with linq,
var Linqdata = from dt in GetData() select dt;
grd.GetStore().DataSource = Linqdata;
grd.DataBind();
}
private object[] GetData()
{
return new object[] {
new object[] { "test1", "test1", true },
new object[] { "test2", "test2", false },
new object[] { "test3", "test3", false } };
}
}

Nested Server Controls to allow nested html

Does anyone know how to make nested server controls accept nested html without "injecting" it out from serverside, ie.
<uc1:CustomServerControl runat="server">
<NestedControl></NestedControl>
<NestedControl2></NestedControl2>
</uc1:CustomServerControl>
but to do this:
<uc1:CustomServerControl runat="server">
<div>
<NestedControl>
</NestedControl>
<NestedControl2></NestedControl2>
</div>
</uc1:CustomServerControl>
Try this:
Section.cs:
[ToolboxData("<{0}:Section runat=\"server\" />")]
public class Section : WebControl, INamingContainer
{
private SectionPartCollection _parts;
[Browsable(false), PersistenceMode(PersistenceMode.InnerProperty)]
public SectionPartCollection Parts
{
get
{
if (this._parts == null)
{
this._parts = new SectionPartCollection();
if (this.IsTrackingViewState)
((IStateManager)this._parts).TrackViewState();
}
return this._parts;
}
}
[Browsable(false), PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate LayoutTemplate { get; set; }
protected override void CreateChildControls()
{
base.CreateChildControls();
if (this.LayoutTemplate != null)
{
this.LayoutTemplate.InstantiateIn(this);
foreach (SectionPart part in this.Parts)
{
Control placeHolder = this.FindControl(part.PlaceHolderID);
if (placeHolder != null)
if (part.ContentTemplate != null)
part.ContentTemplate.InstantiateIn(placeHolder);
}
}
}
protected override void LoadViewState(object savedState)
{
object[] states = (object[])savedState;
base.LoadViewState(states[0]);
if (states[1] != null)
((IStateManager)this.Parts).LoadViewState(states[1]);
}
protected override object SaveViewState()
{
object[] states = new object[2];
states[0] = base.SaveViewState();
if (this._parts != null)
states[1] = ((IStateManager)this.Parts).SaveViewState();
return states;
}
protected override void TrackViewState()
{
base.TrackViewState();
if (this._parts != null)
((IStateManager)this._parts).TrackViewState();
}
}
SectionPart.cs:
[DefaultProperty("PartName")]
public class SectionPart : IStateManager
{
private StateBag _viewState;
private bool _isTrackingViewState;
[DefaultValue("")]
public string PlaceHolderID
{
get { return (string)this.ViewState["PlaceHolderID"] ?? string.Empty; }
set { this.ViewState["PlaceHolderID"] = value; }
}
[Browsable(false), PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ContentTemplate { get; set; }
public void SetDirty()
{
if (this._viewState != null)
this.ViewState.SetDirty(true);
}
[Browsable(false)]
protected StateBag ViewState
{
get
{
if (this._viewState == null)
{
this._viewState = new StateBag(false);
if (this._isTrackingViewState)
((IStateManager)this._viewState).TrackViewState();
}
return this._viewState;
}
}
protected virtual bool IsTrackingViewState
{
get { return this._isTrackingViewState; }
}
protected virtual void LoadViewState(object state)
{
if (state != null)
((IStateManager)this.ViewState).LoadViewState(state);
}
protected virtual object SaveViewState()
{
if (this._viewState != null)
return ((IStateManager)this._viewState).SaveViewState();
return null;
}
protected virtual void TrackViewState()
{
this._isTrackingViewState = true;
if (this._viewState != null)
((IStateManager)this._viewState).TrackViewState();
}
bool IStateManager.IsTrackingViewState
{
get { return this.IsTrackingViewState; }
}
void IStateManager.LoadViewState(object state)
{
this.LoadViewState(state);
}
object IStateManager.SaveViewState()
{
return this.SaveViewState();
}
void IStateManager.TrackViewState()
{
this.TrackViewState();
}
}
SectionPartCollection.cs:
public class SectionPartCollection : StateManagedCollection
{
public SectionPart this[int index]
{
get { return (SectionPart)((IList)this)[index]; }
}
public void Add(SectionPart part)
{
if (part == null)
throw new ArgumentNullException("part");
((IList)this).Add(part);
}
public void Insert(int index, SectionPart part)
{
if (part == null)
throw new ArgumentNullException("part");
((IList)this).Insert(index, part);
}
public void Remove(SectionPart part)
{
if (part == null)
throw new ArgumentNullException("part");
((IList)this).Remove(part);
}
public void RemoveAt(int index)
{
((IList)this).RemoveAt(index);
}
protected override void SetDirtyObject(object o)
{
((SectionPart)o).SetDirty();
}
}
Example:
<uc:Section ID="Section1" runat="server">
<LayoutTemplate>
<table>
<tr>
<td id="TitlePlaceHolder" runat="server">
</td>
</tr>
<tr>
<td id="BodyPlaceHolder" runat="server">
</td>
</tr>
</table>
</LayoutTemplate>
<Parts>
<uc:SectionPart PlaceHolderID="TitlePlaceHolder">
<ContentTemplate>
<span>Title</span>
</ContentTemplate>
</uc:SectionPart>
<uc:SectionPart PlaceHolderID="BodyPlaceHolder">
<ContentTemplate>
<p>
Some content...</p>
</ContentTemplate>
</uc:SectionPart>
</Parts>
</uc:Section>
Try this:
[ToolboxData("..."), ParseChildren(false), PersistChildren(true)]
public class CustomServerControl : WebControl, INamingContainer
{
}
The purpose of this basicly is to have a custom Generic tag library, whick in this case will speak to a underlying object model. The objectmodel is responsible for splitting up articles into its respective parts, ie, Headline, blurb, Images, Comments etc. In this case I'm woring with a collection of them, and by specifying the inner tags, you basicly apply a filter onto what parts of the articles in the section you want to see.
<uc1:Section runat="server">
<HeadLine></HeadLine>
<Blurb></Blurb>
<Body></Body>
</uc1:Section>
Based on what the user specify by only tagging whatever he needs, the respective content will then be written to the frontend.
Now this works wonderfully well, except in one or two cases where you actually need some kind of internal structure to each of the individual parts of an article, and then projecting that onto the entire collection, like having a <table> where the headline should go into one <td> and the rest into another <td>
Hope it makes sense!!