Edit for clarity: I know the code has 2 columns, one for title and one for date, I am trying to change the code so it displays 3 events per row.
I am new to development and I am trying to take an ASP control used in a Sitefinity 6.1 website that currently takes data from our SQL database and outputs it into a single column and instead produce an output in 3 columns.
The .ascx looks like this
<div style="margin:10px">
<asp:MultiView ID="mvEvents" runat="server" ActiveViewIndex="0">
<asp:View ID="viewDefault" runat="server">
<asp:Table ID="tblEvents" runat="server" CellPadding="5">
</asp:Table>
</asp:View>
<asp:View ID="viewList" runat="server">
<asp:Table ID="tblEventsList" runat="server" CellPadding="5">
</asp:Table>
</asp:View>
</asp:MultiView>
</div>
The PageLoad and BuildEvents portions of the .ascx.cs code look like this
protected void Page_Load(object sender, EventArgs e)
{
try
{
CalendarDataContext db = new CalendarDataContext();
var evt = db.CalendarEvents(NumberToDisplay, Department);
foreach (CalendarEventsResult Evt in evt)
{
if (Department == "List")
{
BuildEventsList(Evt.event_name, Evt.event_start_date, Evt.event_idn, Evt.information_id);
}
else
{
BuildEvents(Evt.event_name, Evt.event_start_date, Evt.event_idn, Evt.information_id);
}
}
return;
}
catch(Exception ex)
{
}
}
protected void BuildEvents(string EvtTitle, DateTime EvtStart, int EvtIdn, int EvtInfoIdn)
{
//EvtInfoIdn shows Event Description without location. EvtInfoIdn - 1 shows location information. changing the href to eventInfoId
int EvtInfoId = EvtInfoIdn - 1;
TableRow tr = new TableRow();
tr.VerticalAlign = VerticalAlign.Top;
tr.HorizontalAlign = HorizontalAlign.Center;
TableCell tcTitle = new TableCell();
TableCell tcStart = new TableCell();
StringBuilder sb = new StringBuilder();
sb.AppendLine(ShortMonth(EvtStart.Month));
sb.AppendLine(EvtStart.Day.ToString());
tr.VerticalAlign = VerticalAlign.Bottom;
tr.Height = Unit.Pixel(80);
Literal litDate = new Literal();
litDate.Text = "<div class='EventDate'>" + sb.ToString() + "</div>";
tcStart.Controls.Add(litDate);
Literal litTitle = new Literal();
litTitle.Text = #"<div class='EventTitle'><a style='text-decoration:none;' href='http://events.website.edu/EventList.aspx?view=EventDetails&eventidn=" + EvtIdn.ToString().Trim() + "&information_id=" + EvtInfoId + "&type=&rss=rss'>";
litTitle.Text = litTitle.Text + EvtTitle + "</a></div>";
tcTitle.Controls.Add(litTitle);
tr.Cells.Add(tcStart);
tr.Cells.Add(tcTitle);
tblEvents.Rows.Add(tr);
}
I have tried adding a ColumnSpan = "3" attribute to the ascx file and also tried adding the ColumnSpan to tcStart and tcTitle in the cs file as well in addition to trying to redesign the ascx in design mode and nothing I have though of has worked. I realize this is likely an easy fix, but I am new to C# programming. I appreciate any help provided.
Try the following -- n.b. I haven't been able to test fully as I haven't mocked up your bespoke classes, e.g. CalendarDataContext.
The basic premise is that the CalandarEvents collection is processed in batches of 3 (or whatever number you choose) and that the BuildEvents method now is broken down into smaller parts, each with their own responsibility, which should help with maintenance going forward.
protected void Page_Load(object sender, EventArgs e)
{
try
{
CalendarDataContext db = new CalendarDataContext();
var evt = db.CalendarEvents(NumberToDisplay, Department);
if (Department == "List")
{
foreach (CalendarEventsResult cer in evt)
{
BuildEventsList(cer.event_name, cer.event_start_date, cer.event_idn, cer.information_id);
}
}
else
{
// Depending what type evt is may depend on how to convert to the List.
// This assumes that it supports IEnumerable (like DataTable)
List<CalendarEventsResult> events = evt.AsEnumerable().ToList();
// Rather than hard-coding, read this from config or database
// to allow easier changing to a different number of events per row?
int numberOfEventsPerRow = 3;
// loop increments in steps of 3 (numberOfEventsPerRow)
for (int rowNumber = 0; rowNumber < events.Count; rowNumber += numberOfEventsPerRow)
{
// use Linq to pick out the subset of Events to process
List<CalendarEventsResult> queryEvents = events
.Skip(numberOfEventsPerRow * rowNumber)
.Take(numberOfEventsPerRow).ToList<CalendarEventsResult>();
// Build a row using that subset
TableRow tr = buildRow(queryEvents, numberOfEventsPerRow);
// Add it to the table
tblEvents.Rows.Add(tr);
}
}
}
catch (Exception ex)
{
// this just suppresses any exceptions. Better to fix issues at source.
}
}
private TableRow buildRow(List<CalendarEventsResult> events, int eventsPerRow)
{
TableRow tr = new TableRow();
// Can these be added to CSS?
tr.HorizontalAlign = HorizontalAlign.Center;
tr.VerticalAlign = VerticalAlign.Bottom;
tr.Height = Unit.Pixel(80);
TableCell tc;
// for our event subset, build a pair of cells for each Event,
// adding them to a row
foreach (var evt in events)
{
tc = BuildEventDate(evt);
tr.Cells.Add(tc);
tc = BuildEventTitle(evt);
tr.Cells.Add(tc);
}
// If we're dealing with a partial row, i.e. only 1 or 2 Events,
// pad the row with empty cells.
if (events.Count < eventsPerRow)
{
tc = BuildEmptyCell(eventsPerRow - events.Count);
tr.Cells.Add(tc);
}
return tr;
}
private TableCell BuildEventDate(CalendarEventsResult evt)
{
TableCell tc = new TableCell();
Literal litDate = new Literal();
//String.Format should suffice c.f. StringBuilder
litDate.Text = String.Format("<div class='EventDate'>{0}{1}</div>", ShortMonth(evt.Month), evt.Day.ToString());
tc.Controls.Add(litDate);
return tc;
}
private TableCell BuildEventTitle(CalendarEventsResult evt)
{
TableCell tc = new TableCell();
int evtInfoId = evt.information_id - 1;
Literal litTitle = new Literal();
// n.b. hard-coded URL doesn't look good for ease of maintenance (move to config or CalendarEventsResult?)
litTitle.Text = String.Format(#"<div class='EventTitle'><a style='text-decoration:none;' href='http://events.website.edu/EventList.aspx?view=EventDetails&eventidn={0}&information_id={1}&type=&rss=rss'>{2}</a></div>",
evt.event_idn.ToString().Trim(), evtInfoId, evt.event_name);
tc.Controls.Add(litTitle);
return tc;
}
private TableCell BuildEmptyCell(int numberOfEvents)
{
// Define as const rather than having unexplained "2" hard-coded.
const int spanPerEvent = 2;
TableCell tc = new TableCell();
tc.ColumnSpan = spanPerEvent * numberOfEvents;
tc.Text = "";
return tc;
}
}
Related
I have an asp.net program that creates a simple survey form for users to answer.
Most of the questions use a dropdownlist with answer scores from 1-5 (bad-good) and I'm trying to add an event handler to the dropdownlist objects so that the comments box is only enabled if the user selects a score between 1 and 2.
However when I add the delegate lambda call for the event handler, instead of each dropdownlist affecting their own corresponding comment box, they all seem to point only at the last one added (and they work once, then no more and only the last ddl continues having the expected behaviour).
My code:
//Called from Page_Load
private void PopulateSurvey()
{
btnSubmit.Enabled = true;
List<Question> questions = (from p in context.Questions
join q in context.Survey_Questions on p.ID equals q.QuestionID
where q.SurveyID == surveyid
select p).ToList();
Table tbl = new Table();
tbl.Width = Unit.Percentage(100);
TableRow tr;
TableCell tc;
TableCell tc1;
TableCell tc2;
TextBox txt;
CheckBox cbk;
DropDownList ddl = new DropDownList();
foreach (Question q in questions)
{
if (q.Division.Equals("General") || q.Division.Equals(ddlDivisions.SelectedValue.ToString()))
{
tr = new TableRow();
tc = new TableCell();
tc.Width = Unit.Percentage(55);
tc.Text = q.Text;
tc.Attributes.Add("id", q.ID.ToString());
tr.Cells.Add(tc);
tc = new TableCell();
if (q.QuestionType.ToLower() == "singlelinetextbox")
{
txt = new TextBox();
txt.ID = "txt_" + q.ID;
//txt.Width = Unit.Percentage(40);
tc.Controls.Add(txt);
}
if (q.QuestionType.ToLower() == "multilinetextbox")
{
txt = new TextBox();
txt.ID = "txt_" + q.ID;
txt.TextMode = TextBoxMode.MultiLine;
//txt.Width = Unit.Percentage(40);
tc.Controls.Add(txt);
}
if (q.QuestionType.ToLower() == "singleselect")
{
ddl = new DropDownList();
ddl.ID = "ddl_" + q.ID;
//ddl.Width = Unit.Percentage(41);
if (!string.IsNullOrEmpty(q.Options))
{
string[] values = q.Options.Split(',');
foreach (string v in values)
ddl.Items.Add(v.Trim());
}
ddl.AutoPostBack = true;
tc.Controls.Add(ddl);
}
//tc.Width = Unit.Percentage(60);
tr.Cells.Add(tc);
//Add comment row
tc1 = new TableCell();
//tc.Width = Unit.Percentage(5);
tc1.Text = "Comentario: ";
tc1.Attributes.Add("id", q.ID.ToString());
//tc1.Visible = false;
tr.Cells.Add(tc1);
tc2 = new TableCell();
txt = new TextBox();
txt.ID = "txt_" + q.ID + "comment";
txt.TextMode = TextBoxMode.SingleLine;
//txt.Width = Unit.Percentage(25);
tc2.Controls.Add(txt);
tr.Cells.Add(tc2);
ddl.SelectedIndexChanged+= (sender, e) => ScoreChanged(sender, e, tc1,tc2, ddl.SelectedIndex);
tbl.Rows.Add(tr);
}
}
pnlSurvey.Controls.Add(tbl);
}
protected void ScoreChanged (object sender, EventArgs e, TableCell tc1, TableCell tc2, int score)
{
if( score <2)
{
tc1.Visible = false;
tc2.Visible = false;
}
else
{
tc1.Visible = true;
tc2.Visible = true;
}
}
First thing that comes to mind is maybe the event handler keeps the references themselves,m instead of the orphaned objects created with each question, so all event handlers end up with the same tc1 and tc2 reference and thus only the last object? Is that the case and if so how do I go around it?
Your problem is in this line:
ddl.SelectedIndexChanged +=
(sender, e) => ScoreChanged(sender, e, tc1,tc2, ddl.SelectedIndex);
Two things are important here:
You are using an anonymous method to invoke ScoreChanged.
You are passing tc1 and tc2 as an argument to the ScoreChanged method. You have defined those variables at the beginning of your code block, outside the loop.
The magic word in this context is closure. Since tc1 and tc2 are defined outside the scope of the anonymous method, they turn into closures. That means the will not have the value at the time you define the method, but at the time you invoke it. Since you are constantly overwriting the value of the variables in the foreach loop, at the time of the invokation those variables will have the value of the last iteration of the loop.
The solution is simple: Declare the variables inside the loop. This will create a new closure for each iteration of the foreach:
TableRow tr;
TableCell tc;
TextBox txt;
CheckBox cbk;
foreach (Question q in questions)
{
TableCell tc1;
TableCell tc2;
DropDownList ddl; //Don't forget to include ddl, since you are using its selected index
//...
On a more general note: Don't do "declaration" blocks like this at the beginning of a method in C#. Declare the variable the first time you use it (unless there is a good reason to do otherwise, for example you want it to be a part of a closure). There are many good reasons for that and you just have experienced one of them. Another one would be that when you turn a part of your code to a method with Visual Studio's refactoring feature, you will pass the predeclared variables as ref params. These are the most obvious reasons. There are more.
I am writing a web application in Visual Studio 2015 pro using C# and ASP.NET. Right now, I have it set up where the user will click a button and the C# code will go get a bunch of data then display it back to the user in tables. I have spent a day of work trying to figure out how to add some form of a clickable event to the table rows but have had no success. Ultimately, what I want to do is call a method in my C# code when the table row is clicked and send it the row index.
Here is the C# code I am using for generating the tables:
protected void searchButton_Click(object sender, EventArgs e)
{
try
{
// Remove the error display
resultListLabel.Style.Add("Display", "None");
// Get document groups
groups = TableCommands.getGroups(dbConn, "retriever", searchTextOne.Text, searchTextTwo.Text);
foreach (var dataPair in groups)
{
// Get data pair group names into list
List<string> name = dataPair.Key.Split('|').ToList();
// ====== Make table
Table resultTable = new Table();
resultTable.Attributes["class"] = "displayTable";
resultList.Controls.Add(resultTable);
// ====== Add table info row
TableRow groupInfo = new TableRow();
groupInfo.Attributes["class"] = "groupInfoLabel";
// add row to table
resultTable.Rows.Add(groupInfo);
// create cell with information
TableCell infoCell = new TableCell();
infoCell.Text = "MRN: "+name[0]+", Name: " + name[1];
infoCell.ColumnSpan = 3;
// add cell to row
groupInfo.Cells.Add(infoCell);
// ====== Make column label row
TableRow labelRow = new TableRow();
labelRow.Attributes["class"] = "columnLabel";
// add row to table
resultTable.Rows.Add(labelRow);
// make an array of column lables
string[] cellNames = new string[] { "Visit Date", "Document Type", "Doctor ID" };
// add column lables to row
foreach (string s in cellNames)
{
TableCell labelCell = new TableCell();
labelCell.Text = s;
labelRow.Cells.Add(labelCell);
}
// Add display names to table
foreach(var nameList in dataPair.Value)
{
TableRow nameRow = new TableRow();
nameRow.Attributes["class"] = "columnInfo";
for (int i = 0; i < 3; i++)
{
TableCell nameCell = new TableCell();
nameCell.Text = nameList[i];
nameRow.Cells.Add(nameCell);
}
resultTable.Rows.Add(nameRow);
}
}
}
catch(Exception ex)
{
// Display the error and write to log
resultListLabel.Style.Add("Display", "Inline-Block");
writeLog("Failed to generate tables", ex.ToString());
}
}
Here's what I had to do:
Add a global boolean to called tableCall to the C# code
Move the code in searchButton_Click into an if(tableCall) statement in the Page_Load method.
protected void Page_Load(object sender ,EventArgs e){
...
if(tableCall){
//Do stuff from searchButton_Click
}
...
}
Add tableCall = true; and Page_Load(sender, e) to searchButton_Click
Modify the for loop to add buttons in the cell like so:
// Add display names to table
foreach (var nameList in dataPair.Value)
{
TableRow nameRow = new TableRow();
nameRow.Attributes["class"] = "columnInfo";
// Add display names to table
foreach (var nameList in dataPair.Value)
{
TableRow nameRow = new TableRow();
nameRow.Attributes["class"] = "columnInfo";
for (int i = 0; i < 3; i++)
{
TableCell nameCell = new TableCell();
nameRow.Cells.Add(nameCell);
Button b = new Button();
b.Attributes["class"] = "docButton";
b.Attributes.Add("DWdocid", nameList[3]);
b.Text = nameList[i];
b.Click += new EventHandler((s, ea) => test(s, ea, b.Attributes["DWdocid"]));
nameCell.Controls.Add(b);
}
resultTable.Rows.Add(nameRow);
}
This adds a button to each of the three cells in the row, but adds the same document id to each of the buttons in that row, so anywhere the user clicks on that row (except for the 1px borders) will call a method in the C# code while passing it the document ID. I'm sure that with better css skills somebody could make the button span all three cells.
Ok here is the scenario. I have created a completely data driven Radgrid with only 2 static buttons on the page, Save and Cancel. The Radgrid is created dynamically, as well as all the data in the grid (from MS SQL). Here is the tricky part, I have a template column that will contain a control. The type of control is determined again by the data in SQL. I.e., data is 6, I need to return a RadTextBox populated with data from SQL, 5 = RadComboBox, also populated... you get the jist. I have a total of 50 records, so I have around 50 controls, all populated with data which can change and be saved. That is the hard part. I have been stuck for 2 days trying to figure out how to get to the RadGrids cell level, find the control, determine what type of control it is, retrieve the lastest data from that control and save it back out to the Database. The code works, I just need help finding the controls and saving the data...
I need to hit the Save button, which in turn gets all the data and saves it to db. I cannot show you all my code because the codebehind is close to 600 lines. but I will demonstrate with one.
I am giving the controls IDs based on a unique value from that row, so ID="c" = x where x is the unique value.
page.aspx
<form id="formUnionActivityProtestor" runat="server">
<asp:HiddenField ID="hiCaseId" runat="server" />
<asp:HiddenField ID="hiCaseSequence" runat="server" />
<telerik:RadScriptManager runat="server" ID="RadScriptManager1" ScriptMode="Release">
</telerik:RadScriptManager>
<div id="headDiv">
<h2>Blah blah blah</h2>
<h3>blah zeyblah</h3>
<telerik:RadButton runat="server" ID="btnSaveUA" CausesValidation="true" OnClick="btnSaveUA_Click"
Text="Save Union Activity" Skin="Web20" Font-Size="12px" Width="145" Font-Bold="true">
</telerik:RadButton>
<telerik:RadButton runat="server" ID="btnCancel" OnClientClicking="ReadOnly"
Text="Cancel Changes" Skin="Web20" Font-Size="12px" Width="145" Font-Bold="true">
</telerik:RadButton>
</div>
<hr />
<div id="gridContainer" runat="server">
<asp:PlaceHolder runat="server" ID="PlaceHolder1"></asp:PlaceHolder>
</div>
</form>
page.aspx.cs
protected void Page_Init(object sender, System.EventArgs e)
{
radgrid = new RadGrid();
radgrid.ID = "radgrid";
radgrid.PreRender += new EventHandler(radUAGrid_PreRender);
PlaceHolder1.Controls.Add(radgrid);
this.radgrid.NeedDataSource += new GridNeedDataSourceEventHandler(this.grid_NeedDataSource);
radgrid.ItemDataBound += new Telerik.Web.UI.GridItemEventHandler(this.radgrid_ItemDataBound);
radgrid.MasterTableView.DataKeyNames = new string[] { "q_SortValue" };
radgrid.MasterTableView.AutoGenerateColumns = false;
radgrid.MasterTableView.ShowHeader = false;
radgrid.BorderColor = System.Drawing.Color.Gray;
GridBoundColumn boundColumn;
boundColumn = new GridBoundColumn();
boundColumn.ItemStyle.Width = 600;
boundColumn.ItemStyle.CssClass = "prompt";
boundColumn.DataField = "q_Prompt";
radgrid.MasterTableView.Columns.Add(boundColumn);
GridTemplateColumn templateColumn = new GridTemplateColumn();
templateColumn.ItemTemplate = new TemplateColumn("q_QuestionnaireTypeID");
//templateColumn.ItemStyle.Width = 0;
templateColumn.DataField = "q_QuestionnaireTypeID";
templateColumn.UniqueName = "q_QuestionnaireTypeID";
radgrid.MasterTableView.Columns.Add(templateColumn);
boundColumn = new GridBoundColumn();
boundColumn.Display = false;
boundColumn.ItemStyle.CssClass = "hidecol";
boundColumn.DataField = "t_QuestionnaireTypeDescription";
radgrid.MasterTableView.Columns.Add(boundColumn);
}
public partial class TemplateColumn : System.Web.UI.Page ,ITemplate //adding template fields
{
string fieldName = "";
int controlTypeID = 0;
DataTable dt;
int counter = 1;
UnionActivity refMgr = new UnionActivity(Global.ICEConnectionString);
public TemplateColumn(string fieldName)
{
this.fieldName = fieldName;
}
public int getQuestionTypeID(int count)
{
int k = (from DataRow dr in dt.Rows.OfType<DataRow>()
where (int)dr["q_SortValue"] == count
select (Int32)dr["q_QuestionnaireTypeID"]).FirstOrDefault();
return k;
}
public void InstantiateIn(Control container)
{
if (counter == 1)
{
dt = UnionActivityDataTable.dt;
}
controlTypeID = getQuestionTypeID(counter);
if (controlTypeID == 5)
{
int QID = (from DataRow dr in dt.Rows.OfType<DataRow>()
where (int)dr["q_SortValue"] == counter
select (int)dr["q_QuestionnaireInstanceID"]).FirstOrDefault();
int QQID = (from DataRow dr in dt.Rows.OfType<DataRow>()
where (int)dr["q_SortValue"] == counter
select (int)dr["q_QuestionnaireInstanceQuestionID"]).FirstOrDefault();
string answer = (from DataRow dr in dt.Rows.OfType<DataRow>()
where (int)dr["q_SortValue"] == counter
select (string)dr["a_Answer"]).FirstOrDefault();
DataTable dt1;
dt1 = getDropDownList(QID, QQID);
RadComboBox cb = new RadComboBox();
foreach (DataRow row in dt1.Rows)
{
RadComboBoxItem item = new RadComboBoxItem();
item.Text = row["DisplayValue"].ToString();
item.Value = row["DDID"].ToString();
if (answer == item.Text)
{
cb.SelectedValue = item.Value;
}
cb.Items.Add(item);
item.DataBind();
}
string x = (from DataRow dr in dt.Rows.OfType<DataRow>()
where (int)dr["q_SortValue"] == counter
select Convert.ToString((int)dr["a_QuestionnaireInstanceQuestionID"])).FirstOrDefault();
cb.ID = "c" + x;
container.Controls.Add(cb);
}
}
DataTable getDropDownList(int QID, int QQID)
{
DataTable dt2 = new DataTable();
try
{
using (refMgr)
{ //retrieving qicr_QuestionnaireInstanceCaseReferenceID
using (DataTable getDropDownData = refMgr.DynamicDropDownData(QID, QQID))
{
if (getDropDownData != null)
{
dt2 = getDropDownData;
}
}
}
}
catch (Exception ex)
{
}
return dt2;
}
}
after page load I look at the source and this is the insert for the combobox...
<td class="rcbInputCell rcbInputCellLeft" style="width:100%;">
<input name="radgrid$ctl00$ctl22$c12" type="text" class="rcbInput radPreventDecorate" id="radgrid_ctl00_ctl22_c12_Input" value="Kiosk" readonly="readonly" />
</td>
I need to attach a method to the save button, but I dont know the first place to start. Telerik is good about getting the page built dynamically, but not saving the data back out. (or even finding the controls...)
I have seen something like this done for survey generation. The only difference is that it wasn't using a Grid. Is there a reason why you need to use a grid rather than just dynamically building the controls on the page?
I can suggest a way so that you can easily obtain the values from dynamic controls.
You can introduce an interface that all your survey controls need to implement.
interface ISurveyControl
{
// expose some common properties
int QuestionID {get; set;}
object Value {get; set;}
// and others as required
}
Then create an extension for every type of control that you need in your survey
public class SurveyTextBox : RadTextBox, ISurveyControl
{
public int QuestionID {get; set;}
public object Value
{
get { return Text; }
set { Text = value.ToString(); }
}
}
public class SurveyComboBox : RadComboBox, ISurveyControl
{
public int QuestionID {get; set;}
public object Value
{
get { return SelectedValue; }
set { SelectedValue = value.ToString(); }
}
}
Make sure you use these extended controls when building the survey and populate the common properties correctly.
Then all you need is a helper function to find all ISurveyControl controls from a container, regardless of whether it's a grid or a page.
List<ISurveyControl > FindSurveyControls(Control container)
{
// you can use linq to find all ISurveyControl within the container
// you may need to make this recursive as well
}
You can then iterate through the controls on save, knowing that they hold enough information such as the QuestionID and so on.
What I'm trying to achieve is a custom calendar where I place events.
I've created a basic custom control which simply lists the events:
namespace MyControls
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
public class Calendar : CompositeDataBoundControl
{
protected override Int32 CreateChildControls(IEnumerable dataSource, Boolean dataBinding)
{
Int32 itemCounter = 0;
if (dataSource != null)
{
IEnumerator dataSourceEnumerator = dataSource.GetEnumerator();
while (dataSourceEnumerator.MoveNext())
{
LinkButton eventLink = new LinkButton();
eventLink.Click += new EventHandler(EventLinkClick);
HtmlGenericControl eventContainer = new HtmlGenericControl();
eventContainer.Controls.Add(eventLink);
eventContainer.TagName = "p";
this.Controls.Add(eventContainer);
if (dataBinding)
{
CalendarEvent currentEvent = (CalendarEvent) dataSourceEnumerator.Current;
eventLink.CommandArgument = String.Concat(currentEvent.Name, "ยง", currentEvent.Day.ToBinary());
eventLink.Text = currentEvent.Name;
eventLink.ToolTip = currentEvent.Description;
}
itemCounter++;
}
}
return itemCounter;
}
protected void EventLinkClick(Object sender, EventArgs e)
{
}
}
}
The control works, when I pass it a List<CalendarEvent> it displays every event's LinkButton inside its own <p />, when I click a LinkButton the EventLinkClick method gets called, and after the postback the LinkButtons are still there with their values.
However, I don't need a plain list of the event, I need to place my events inside a calendar, inside the correct day.
I create my calendar like this:
Int32 year = 2011;
Table monthTable = null;
TableRow weekRow = null;
for (DateTime day = new DateTime(year, 1, 1); day.Year == year; day = day.AddDays(1))
{
if (day.Day == 1)
{
HtmlGenericControl monthName = new HtmlGenericControl();
monthName.InnerText = CultureInfo.CurrentUICulture.DateTimeFormat.GetMonthName(day.Month);
monthName.TagName = "h2";
this.Controls.Add(monthName);
monthTable = new Table();
TableHeaderRow headerRow = new TableHeaderRow();
headerRow.TableSection = TableRowSection.TableHeader;
monthTable.Rows.Add(headerRow);
for (Int32 i = 0; i < 7; i++)
{
TableHeaderCell dayOfWeekCell = new TableHeaderCell();
dayOfWeekCell.Text = CultureInfo.CurrentUICulture.DateTimeFormat.GetShortestDayName((DayOfWeek) i);
headerRow.Cells.Add(dayOfWeekCell);
}
weekRow = new TableRow();
weekRow.TableSection = TableRowSection.TableBody;
for (Int32 i = 0; i < (Int32) day.DayOfWeek; i++)
{
weekRow.Cells.Add(new TableCell());
}
}
if (day.DayOfWeek == DayOfWeek.Sunday && day.Day != 1)
{
monthTable.Rows.Add(weekRow);
weekRow = new TableRow();
weekRow.TableSection = TableRowSection.TableBody;
}
TableCell dayCell = new TableCell();
dayCell.Text = Convert.ToString(day.Day);
weekRow.Cells.Add(dayCell);
if (day.Day == DateTime.DaysInMonth(day.Year, day.Month))
{
for (Int32 i = (Int32) day.DayOfWeek; i < 6; i++)
{
weekRow.Cells.Add(new TableCell());
}
monthTable.Rows.Add(weekRow);
this.Controls.Add(monthTable);
}
}
which yields to something like this:
.
Now, how can I integrate the two things?
What I came up with is casting the dataSource parameter to IEnumerable<CalendarEvents> and after the dayCell.Text = Convert.ToString(day.Day); line I get the events of the day from the IEnumerable<CalendarEvents> through LINQ.
However, this breaks on postback because when the control is recreating itself after a postback the dataSource parameter is full of nulls, so I can't fetch the events of the day, so I can't recreate the controls.
I couldn't find anything on the net about this, and I'm completely stuck.
Am I missing (or messing) something? What should I do to achieve what I'm looking for?
Update #1
As StriplingWarrior suggested I tried to save the dataSource in the ViewState, however I failed dramatically.
What I tried is this: at the beginning of the CreateChildControls method I placed
if (dataBinding)
{
this.ViewState.Add("myDataSource", dataSource);
}
IEnumerable myDataSource = (IEnumerable) this.ViewState["myDataSource"];
and replaced every call to dataSource with myDataSource.
However, when the page post backs this.ViewState["myDataSource"] is null, and I'm back to square one.
I'm starting to regret when I decided to go with a CompositeDataBoundControl... :\
Update #2
I tried to create a new project containing only the custom control, and I rewrote it from scratch, and StriplingWarrior's suggestion worked:
if (dataBinding)
{
this.ViewState.Add("DataSource", dataSource);
}
else
{
dataSource = (IEnumerable) this.ViewState["DataSource"];
}
However, I haven't been able to pinpoit what was causing the this.ViewState["DataSource"] in the original solution.
You're running into ViewState issues. You can either disable viewstate on your control, or write the control in such a way that it saves the information it needs in ViewState, so it doesn't need to rebind to the data source on subsequent postbacks.
After some minor difficulties with my basic test rating control not working because of the missing inline CSS stylesheet, I am trying to dynamically add a bunch of rating controls in a updatepanel when I click on a button in a different updatepanel. (These panels are both in a parent updatepanel, I have defined the triggers and set the updatemode to conditional). Anyways, when I click the button, he updates the updatepanel with the rating controls, but when I hover over them, he always displays 0 (the current rating), and does not change the rating control star image (filledStarRating). The code relevant to my problem (two methods):
protected void imbformulier_Click(Object sender, ImageClickEventArgs e)
{
imbFormulier.Visible = false;
imbGebruikers.Visible = false;
imbModellen.Visible = false;
pnlGegevens1.Visible = false;
pnlGegevens2.Visible = true;
pnlNavigatie.Visible = true;
pnlEval.Visible = true;
//kijken welk formulier moet ingevuld worden adhv de ddl's en dit meegeven aan de zelfgemaakte klasse
List<EvaluatieFormulier> mijnformulieren = (List<EvaluatieFormulier>)Session["mijnformulieren"];
IEnumerator<EvaluatieFormulier> enumerator = mijnformulieren.GetEnumerator();
EvaluatieFormulier meetegeven = new EvaluatieFormulier();
while (enumerator.MoveNext())
{
if (((enumerator.Current.GebruikergeevalueerdID == ddlGebruikers.SelectedValue) && (enumerator.Current.ModelID == Convert.ToInt32(ddlModellen.SelectedValue))))
{
meetegeven = enumerator.Current;
//Eventueel tekst veranderen als er al was gewerkt aan een bepaalde evaluatie
if (meetegeven.Tijdaangewerkt == 0)
{
lblInfo.Text = "Evaluatie gestart van " + ddlGebruikers.SelectedItem.Text;
lblDatum.Text = "Evaluatie begonnen op: "+ DateTime.Now.Date.ToString("d/M/yyyy") + "(Vandaag)" ;
//updaten in database
Session["aantalminutenaangewerkt"] = 0;
Session["aantalsecondenaangewerkt"] = 0;
timTijdAanGewerkt.Enabled = true;
lblTijd.Visible = true;
}
else
{
lblInfo.Text = "Evaluatie verdergezet van " + ddlGebruikers.SelectedItem.Text;
lblDatum.Text = "Evaluatie laatst gewijzigd : " + meetegeven.Tijdingevuld.ToString();
Session["aantalminutenaangewerkt"] = meetegeven.Tijdaangewerkt;
Session["aantalsecondenaangewerkt"] = 0;
timTijdAanGewerkt.Enabled = true;
lblTijd.Visible = true;
}
}
}
//Rating controls aanmaken voor elke criteria
List<AjaxControlToolkit.Rating> lijstratingcontrols = new List<AjaxControlToolkit.Rating>();
Model modelmetdomeinen = new Model() ;
IEnumerator<Model> modelenum = Database.laadModellenIn().GetEnumerator();
while (modelenum.MoveNext())
{
if (modelenum.Current.ModelID == meetegeven.ModelID)
modelmetdomeinen = modelenum.Current;
}
//foreach (Domein domein in modelmetdomeinen.Domeins)
//{
// foreach (Criterium criterium in domein.Criteriums)
// {
// AjaxControlToolkit.Rating ratingcontrol = new AjaxControlToolkit.Rating();
// ratingcontrol.ID = criterium.CriteriumNaam;
// ratingcontrol.StarCssClass = "ratingStar";
// ratingcontrol.EmptyStarCssClass = "emptyStarRating";
// ratingcontrol.WaitingStarCssClass = "emptyStarRating";
// ratingcontrol.FilledStarCssClass = "filledStarRating";
// ratingcontrol.Changed += new AjaxControlToolkit.RatingEventHandler(rating_Changed);
// ToolkitScriptManager1.RegisterAsyncPostBackControl(ratingcontrol);
// lijstratingcontrols.Add(ratingcontrol);
// }
//}
//Evaluatieform formulier = new Evaluatieform(meetegeven,lijstratingcontrols);
Table evaluatietabel = new Table();
int domeinteller =0;
foreach (Domein domein in modelmetdomeinen.Domeins)
{
domeinteller++;
if (domeinteller < 4)
{
TableRow domeinrij = new TableRow();
TableCell domeintitel = new TableCell();
domeintitel.Text = domeinteller + ". " + domein.DomeinNaam;
domeintitel.BorderStyle = BorderStyle.None;
domeinrij.Cells.Add(domeintitel);
evaluatietabel.Rows.Add(domeinrij);
foreach (Criterium criterium in domein.Criteriums)
{
int criteriumteller = 1;
TableRow criteriumrij = new TableRow();
TableCell criteriumtitel = new TableCell();
TableCell opvulcell = new TableCell();
TableCell ratingcell = new TableCell();
criteriumtitel.BorderStyle = BorderStyle.None;
opvulcell.BorderStyle = BorderStyle.None;
ratingcell.BorderStyle = BorderStyle.None;
criteriumtitel.Text = criteriumteller + ". " + criterium.CriteriumNaam;
AjaxControlToolkit.Rating ratingcontrol = new AjaxControlToolkit.Rating();
ratingcontrol.ID = criterium.CriteriumNaam;
ratingcontrol.StarCssClass = "ratingStar";
ratingcontrol.EmptyStarCssClass = "emptyStarRating";
ratingcontrol.WaitingStarCssClass = "emptyStarRating";
ratingcontrol.FilledStarCssClass = "filledStarRating";
ratingcontrol.Changed += new AjaxControlToolkit.RatingEventHandler(rating_Changed);
ratingcell.Controls.Add(ratingcontrol);
ratingcell.Attributes.Add("runat", "server");
ratingcell.Attributes.Add("onclick", "return false");
criteriumrij.Cells.Add(opvulcell);
criteriumrij.Cells.Add(criteriumtitel);
criteriumrij.Cells.Add(ratingcell);
evaluatietabel.Rows.Add(criteriumrij);
criteriumteller++;
}
}
}
evaluatietabel.BorderStyle = BorderStyle.None;
pnlEval.ContentTemplateContainer.Controls.Add(evaluatietabel);
Session["formulieractief"] = true;
pnlEval.Update();
pnlGegevens1.Update();
pnlGegevens2.Update();
}
I'm also adding the link to an image that shows my problem, so that you can see it for yourself:
We had the same issue recently and the problem was that the latest version of the AjaxControltoolkit seems to require a ToolkitScriptManager instead of the normal ScriptManager.
So just try changing your
asp:ScriptManager tag to
asp:ToolKitScriptManager or
ajax:ToolKitScriptManager
or whatever your namespace is.