背景:#EDF0F5 #FAFBE6 #FFF2E2 #FDE6E0 #F3FFE1 #DAFAF3 #EAEAEF 默认  
阅读新闻

List控件中使用日期选择

[日期:2003-10-03] 来源:DotNetJunkies  作者:Doug Seven [字体: ]
Ask the DotNetJunkies: Using a Pop-Up Calendar and a List Control    Send this to a friend    Printable Version

By Doug Seven, Microsoft ASP.NET MVP
Published Date:
1/9/2003
Tested With:
ASP.NET v1
Check or Rate Page:
No VB.NET Download Download C# Code Rate This Article

The Question:

In a DataGrid, when the user clicks on the Edit button, a cell (TableCell) and an image link are created at runtime. When the user clicks on the image, a calendar pops up. It closes itself when the user clicks on a date. My problem is that I don't know how to populate the cell with the date that the user chooses from the calendar. I can, however, get the date from the calendar to populate the text outside the datagrid. I'd be appreciate if you coud show me some samples on how to achieve this. Thanks.

Wally Stem

The Answer:

Truthfully, if you can populate a TextBox outside of a DataGrid, you have done all of the work. The only thing your missing is how to correctly identify the control inside the DataGrid that you want to put the date into. DataGrids implement the INamingContainer interface, which means that the DataGrid dynamically renames its child controls to prevent any naming conflicts. If you look at the source code for the Web Form shown in Figure 1 you will see what I mean.

Figure 1

 

In Figure 1 is a DataGrid displaying the first 10 records from the Northwind Orders table, with the third record in edit mode. In the rendered HTML you can see how the TextBox (which has the ID value of txtDate) has been rendered as shown here:

<INPUT id="DataGrid1__ctl4_txtDate" type="text" value="7/12/1996" name="DataGrid1:_ctl4:txtDate">

You can see how the TextBox's ID value was changed to "DataGrid1:_ctl4:txtDate" - this is what the INamingContainer interface does. With this in mind, the soluion to the problem is to capture the rendered name of the TextBox at runtime and use it.

How It Works

When the Web Form is rendered with a DataGrid in edit mode you need to:

  1. Link to a JavaScript file with the CalendarPopUp function
  2. Render a JavaScript link in the row that is being edited

The JavaScript file is included in the source code (CalendarPopUp.js) for this article, and is shown here:

function pickDate(Src){
    window.open("CalendarPopUp.aspx?src=" + Src, "_blank", "height=260, width=250, left=100, top=100, " +
    "location=no, menubar=no, resizable=no, " +
    "scrollbars=no, titlebar=no, toolbar=no", true);
}

When the pickDate() function is invoked, a new browser window is opened, without the navigation bars (as seen in Figure 2), and a Web Form showing the calendar is rendered.

Figure 2

The Web Form

The Web Form (WebForm1.aspx) has a DataGrid with a single column set up for editing, the last column - the Shipped Date column. In the EditItemTemplate I have defined a single TextBox and assigned it the ID value of txtDate. When this EditItemTemplate is rendered, this TextBox will be renamed, as we saw above.

<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="CalendarPopUp.WebForm1" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
  <TITLE>WebForm1</TITLE>
  <LINK rel="stylesheet" type="text/css" href="Css/ie50.css">
  <SCRIPT src="CalendarPopUp.js" type="text/javascript"></SCRIPT>
</HEAD>
<body MS_POSITIONING="FlowLayout">
<FORM id="Form1" method="post" runat="server">
  <asp:DataGrid id="DataGrid1" runat="server" CssClass="Normal" AutoGenerateColumns="False" BorderStyle="Solid" GridLines="Horizontal" BorderColor="#404040" CellPadding="4">
    <AlternatingItemStyle BackColor="#E0E0E0"></AlternatingItemStyle>
    <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="Navy"></HeaderStyle>
    <Columns>
      <asp:EditCommandColumn ButtonType="LinkButton" UpdateText="Update" CancelText="Cancel" EditText="Edit"></asp:EditCommandColumn>
      <asp:BoundColumn DataField="OrderID" ReadOnly="True" HeaderText="Order ID"></asp:BoundColumn>
      <asp:BoundColumn DataField="CustomerID" ReadOnly="True" HeaderText="Customer ID"></asp:BoundColumn>
      <asp:BoundColumn DataField="OrderDate" ReadOnly="True" HeaderText="Order Date" DataFormatString="{0:d}"></asp:BoundColumn>
      <asp:TemplateColumn HeaderText="Shipped Date">
        <ItemTemplate>
          <asp:Label runat="server" Text='<%# DataBinder.Eval(Container, "DataItem.ShippedDate", "{0:d}") %>'>
          </asp:Label>
        </ItemTemplate>
        <EditItemTemplate>
          <asp:TextBox runat="server" id="txtDate" Text='<%# DataBinder.Eval(Container, "DataItem.ShippedDate", "{0:d}") %>'>
          </asp:TextBox>
        </EditItemTemplate>
      </asp:TemplateColumn>
    </Columns>
  </asp:DataGrid>
  <P></P>
  <P><asp:Label id="Label1" runat="server"></asp:Label></P>
</FORM>
</body>
</HTML>

 

The Event Handlers

Let me break down all of the event handlers for this Web Form.

The Page_Load() Event Handler

In the Page_Load() event handler I simply invoke the BindData() method whenever the request is NOT a postback. In the BindData methid I create a connection to my Northwind database and I bind the results of a SQL command to the DataGrid.

private void Page_Load(object sender, System.EventArgs e) {
  if (! Page.IsPostBack)
    BindData();
}

private void BindData() {
  SqlConnection con=newSqlConnection("server=localhost;database=Northwind;uid=sa;pwd=;");
  SqlCommand cmd = new SqlCommand("SELECT TOP 10 * FROM Orders", con);
  try {
    con.Open();
    DataGrid1.DataSource = cmd.ExecuteReader();
    DataGrid1.DataBind();
    con.Close();
  }
  catch (Exception ex) {
    Trace.Warn(ex.Message);
  }
}

 

The InitializeComponent() Method

In order for the next few event handlers to work properly, I edited the InitializeComponent() method that is created by Visual Studio .NET. I hade to register the new DataGrid event handlers I am adding.

private void InitializeComponent() {
  this.DataGrid1.CancelCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.DataGrid1_CancelItemCommand);
  this.DataGrid1.EditCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.DataGrid1_EditItemCommand);
  this.DataGrid1.UpdateCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.DataGrid1_UpdateItemCommand);
  this.DataGrid1.ItemDataBound += new System.Web.UI.WebControls.DataGridItemEventHandler(this.DataGrid1_OnItemDataBound);
  this.Load += new System.EventHandler(this.Page_Load);
}

 

The DataGrid1_EditItemCommand() Handler

In the DataGri1_EditItemCommand handler I simply set the EditItemIndex property of the DataGrid and rebind the data.

private void DataGrid1_EditItemCommand(object sender, System.Web.UI.WebControls.DataGridCommandEventArgs e) {
  DataGrid1.EditItemIndex = e.Item.ItemIndex;
  BindData();
}

 

The DataGrid1_CancelItemCommand() Event Handler

In this event handler I simply reset the DataGrid to non-edit mode and rebind the data.

private void DataGrid1_CancelItemCommand(object sender, System.Web.UI.WebControls.DataGridCommandEventArgs e) {
  DataGrid1.EditItemIndex = -1;
  BindData();
}

 

The DataGrid1_UpdateItemCommand() Event Handler

In this event handler, I capture the value submitted in the txtDate TextBox and - for the purposes of this sample - render the value to a Label control. Normally you would use the value to update your data source (as is indicated by a comment in the code).

private void DataGrid1_UpdateItemCommand(object sender, System.Web.UI.WebControls.DataGridCommandEventArgs e) {
  Int32 iLastCellIndex = e.Item.Cells.Count-1; //The Index for the last cell
  String sNewDate = ((TextBox)e.Item.Cells[iLastCellIndex].FindControl("txtDate")).Text; //Get the value of the TextBox
  Label1.Text = "You set the date " + sNewDate;
  //Add database updating here
  DataGrid1.EditItemIndex = -1;
  BindData();
}

 

The DataGrid1_OnItemDataBound() Event Handler

In the DataGrid1_OnItemDataBound event handler I do a little more work. Here I render the link tag and image tag for the calendar pop-up link. I need to pass the name of my TextBox control to the CalendarPopUp.aspx Web Form so that that Web Form know which control it has to pass a value back to. This is where I have to know the name of the TextBox as it is rendered to the client. I can use the Control.ClientID property. This property (which is inherited by all Web controls) returns the name of the HTML element that is rendered to the client. That's the big secret...that's how you can get the rendered name of a control embedded in a parent control that implements INamingContainer.

Using the ClientID value I can render the link tag, passing the ClientID value to the pickDate() javascript function.

private void DataGrid1_OnItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e) {
  if ( e.Item.ItemType == ListItemType.EditItem ) {
    Int32 iLastCellIndex = e.Item.Cells.Count-1; //The Index for the last cell
    String sTextBoxName = e.Item.Cells[iLastCellIndex].FindControl("txtDate").ClientID; //The rendered name of the TextBox
    String sLink = "<A href="\"javascript:pickDate('"" ??);\? +
      sTextBoxName +
      "');\"><IMG src='\"i/iCalendar.gif\"' align='\"absmiddle\"' border='\"0\"'></A>"; //The HTML to add to the Edit cell
    e.Item.Cells[iLastCellIndex].Controls.Add(new LiteralControl(sLink)); //Add the HTML
  }
}

 

The CalendarPopUp.aspx Web Form

The CalendarPopUp.asx Web Form displays a Calendar control and a LinkButton control. After a date is selected in the Calendar, and the LinkButton is clicked, an event handler is used to rewrite the HTML output of the page. The new HTML closes the page and passes the selected value back to the originating page and control.

private void LinkButton1_Click(object sender, System.EventArgs e) {
  System.Text.StringBuilder sbScript = new System.Text.StringBuilder();
  sbScript.Append("<SCRIPT language="javascript">"); //Opening script tag
  sbScript.Append(Environment.NewLine); //Newline
  sbScript.Append("window.opener.document.all['"); //Reference to original window
  sbScript.Append(Request.QueryString["src"]); //Control to set value on
  sbScript.Append("'].value = '"); //Set the value
  sbScript.Append(Calendar1.SelectedDate.ToShortDateString()); //The date
  sbScript.Append("';"); //End of line
  sbScript.Append(Environment.NewLine); //Newline
  sbScript.Append("window.close();"); //Close this window
  sbScript.Append(Environment.NewLine); //Newline
  sbScript.Append("</SCRIPT> "); //Closing script tag
  //Add the script to the page
  this.Page.Controls.Add(new LiteralControl(sbScript.ToString()));
}

 

Summary

To use a pop-up calendar, I implemented some basic javascript and opened a new browser window. When I opened the window, I passed a reference to the Web control I want to put the date into. When the pop-up window is closed, it passed the date value back to the correct control. For DataGrid embedded controls I used the Control.ClientID property to get the rendered name of the Web control I am modifying. The ClientID value is the ID of the control as it appears in the browser source text, which may not be the same as the ID value assigned to the control in code.

阅读:
录入:木鸟

推荐 】 【 打印
上一篇:
下一篇:Using Custom Attributes for Validation
相关新闻      
本文评论       全部评论
发表评论


点评: 字数
姓名:

  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款