CalendarExtender of AJAX Control Toolkit (for now, ver 30390) has not a default feature to pick up multiple dates.  Vince Xu of MSFT gave an answer in asp.net forum. It helps me much. But, there are some issues in his sample code.

  1. if clean up the TextBox1 and then add a new date, the old text of TextBox1 will show up again in the textBox1, because the HiddenField1 is not cleaned up at all.
  2. The “Done” button seems not reasonal. But, without the “done” button, the opened calendar could not be hidden forever.

I fixed them by making a little change. Here is the code sample:

<ajaxToolkit:ToolkitScriptManager runat=”Server” EnablePartialRendering=”true” ID=”ScriptManager1″ />

<asp:TextBox ID=”TextBox1″ runat=”server”  ></asp:TextBox>

<ajaxToolkit:CalendarExtender ID=”calendar1″ runat=”Server” BehaviorID=”Calendar1″
TargetControlID=”TextBox1″
OnClientDateSelectionChanged=”dateselect” OnClientHidden=”calendarhidden” OnClientShown=”setInitialValue” />

<script type=”text/javascript”>
var tmpDates = “”;

function setInitialValue() {
tmpDates = $get(‘<%=TextBox1.ClientID %>’).value;
}
function dateselect(ev) {
var calendarBehavior1 = $find(“Calendar1″);
var date = calendarBehavior1._selectedDate.format(“M/dd/yyyy”);
if (tmpDates.indexOf(date) < 0) {
if (tmpDates != “”) { $get(‘<%=TextBox1.ClientID %>’).value = tmpDates + “,” + date; }
else { $get(‘<%=TextBox1.ClientID %>’).value = date; }
}
else { $get(‘<%=TextBox1.ClientID %>’).value = tmpDates; }
}
function calendarhidden(obj) {
var tbxValue = $get(‘<%=TextBox1.ClientID %>’).value;
calendarBehavior = $find(“Calendar1″);
if (tmpDates.toString() != tbxValue.toString()) {
calendarBehavior.show();
}
}

</script>

The URL with a  QueryString is something like “apage.aspx?month=10&year=2006&…”, anything in Request.QueryString cannot be changed because it is readonly, but the values of “month” and “year” can be changed as followed:

NameValueCollection nvc = HttpUtility.ParseQueryString(Request.QueryString.ToString());

nvc.Set(“month”, “12″);

nvc.Set(“year”, “2009″);

string newUrl = this.Request.ServerVariables["URL"].ToString() + “?” + nvc.ToString() ;

TreeView can be bound with XmlDataSource and SiteMapDataSource controls (vs2008exp). But the data come up from the database, a strongly typed DataSet. So, manually populate a TreeView from a DataTable object, instead of auto binding:

int currYear = 0;
TreeNode curRootNode = new TreeNode();
int i = 0;

do
{
rw = dt.Rows[i];
if (rw.theYear != currYear)
{
currYear = rw.theYear;
TreeNode root = new TreeNode(currYear);
curRootNode = root;
this.TreeView1.Nodes.Add(root);
TreeNode child = new       TreeNode(Enum.GetName(typeof(ShortMonthName),rw.theMonth) + “. [" +       rw.itemNumber + "]“);
curRootNode.ChildNodes.Add(child);
}
else
{
TreeNode child = new TreeNode(Enum.GetName(typeof(ShortMonthName), rw.theMonth) + “. [" + rw.itemNumber + "]“);
curRootNode.ChildNodes.Add(child);
}
i++;
} while (i < dt.Rows.Count);

Here is the result:

archiveTreeView

If use the following query script for creating a TableAdapter method, like “GetDataById”, when using OLEDB:

“SELECT * FROM aTable WHERE theID=@id”

an error will happen:

“error in where clause near ‘@’ unable to parse query text”

The solution is to use “?”, instead of “@variablename”, so teh following will succeed:

“SELECT * FROM aTable WHERE theID=?”