本篇文章會繼續講解窗體控件的使用。
表格與編輯窗體
表格與編輯窗體的交互在項目中經常需要用到,為此FineUI專門為表格控件擴展了WindowField列,下面是一個典型的例子:
1: <ext:Grid ID="Grid2" Title="Grid2" PageSize="80" ShowBorder="false" AllowPaging="true"
2: OnPageIndexChange="Grid2_PageIndexChange" ShowHeader="False" runat="server" EnableCheckBoxSelect="True"
3: DataKeyNames="Id,Name" OnSort="Grid2_Sort" EnableRowNumber="True">
4: <Columns>
5: // 省略其他列...
6: <ext:WindowField TextAlign="Center" Width="60px" WindowID="Window1" Icon="Pencil"
7: ToolTip="編輯" DataIFrameUrlFields="Id,Name" DataIFrameUrlFormatString="../grid/grid_iframe_window.aspx?id={0}&name={1}"
8: Title="編輯" IFrameUrl="~/alert.aspx" />
9: </Columns>
10: </ext:Grid>
11:
12: <ext:Window ID="Window1" Title="彈出窗體" Hidden="true" EnableIFrame="true" IFrameUrl="about:blank"
13: EnableMaximize="true" Target="Top" EnableResize="true" runat="server" OnClose="Window1_Close"
14: IsModal="true" Width="750px" EnableConfirmOnClose="true" Height="550px">
15: </ext:Window>
表格控件WindowField列有如下屬性需要特別關注:
- WindowID:點擊此字段彈出的窗體ID
- DataIFrameUrlFields:綁定到窗體IFrame地址的字段名稱列表
- DataIFrameUrlFormatString:綁定到窗體IFrame地址的字段格式化字符串
還有其他一些屬性我們可能會用到:
- UrlEncode:對每個綁定到IFrame地址的字段進行URL編碼(默認為true)
- DataWindowTitleField:綁定到窗體標題的字段名稱
- DataWindowTitleFormatString:綁定到窗體標題的字段格式化字符串
- Enabled:是否可用(如果不可用,則會被渲染為靜態文本)
- IFrameUrl:窗體IFrame地址
- Title:窗體標題
- Icon:窗體圖標
- IconUrl:窗體圖標地址
其中UrlEncode屬性用來對地址參數進行編碼,比如本例中Major可能是“材料科學學院”,則生成的IFrame地址為 http://gsa.ustc.edu.cn/search?q=%E6%9D%90%E6%96%99%E7%A7%91%E5%AD%A6%E5%AD%A6%E9%99%A2
如果作為IFrame地址的字段包含空格、中文或者其他特殊字符,則這個屬性有助於生成瀏覽器可用的有效鏈接地址。
Window控件需要特別注意的屬性是EnableIFrame、IFrameUrl,如果需要在窗體關閉進行處理還需要注冊OnClose事件處理函數(CloseAction屬性的默認值是HidePostBack,不需要特別設置)。
由於默認窗體IFrame中不需要加載任何頁面,所以設置IFrameUrl為about:blank.
彈出編輯窗體時的界面效果:
子窗體與父窗體傳值
這里的子窗體指的是包含IFrame的彈出子窗體,由於子窗體包含的頁面和父窗體屬於兩個不同的頁面,這就涉及Asp.Net兩個頁面如何傳值的問題。
- 父頁面向子頁面傳值:這個比較簡單,一般通過地址欄參數傳遞,比如父頁面是列表頁面list.aspx,編輯某一條目時地址為edit_item.aspx?id=322
- 父頁面向子頁面傳值:
- 臨時Session,在關閉子頁面之前,將要保存的數據存入Session中,Session("edit_result1") = "1111"; 然后在父頁面中獲取此保存的值(需要刷新或者回發父頁面)Session("edit_result1").ToString()
- 臨時Cookie,方法同Session,只不過Cookie是將數據保存在客戶端而不是服務器,但是有大小限制
- JavaScript傳值,其實臨時Session和臨時Cookie都不是好的解決辦法,不僅性能有問題而且編碼復雜。由於子頁面和父頁面位於相同的域名下(不違反JavaScript的同源策略),所以可以直接在子頁面中查找父頁面的表單字段,並將結果保存到父頁面的表單字段中。
FineUI充分理解這種需求,並提供了方便的函數來幫助我們在父窗體向子窗體傳值,大致步驟如下:
- 告訴子窗體,父窗體中可以被作為接受結果數據的表單字段有哪些
- 打開子窗體
- 用戶操作后得到結果,將結果保存到父窗體中接受數據的表單字段
- 關閉子窗體
下面通過一個示例來認識這一過程:
父頁面代碼:
1: <ext:SimpleForm ID="SimpleForm1" Title="表單" EnableBackgroundColor="true" BodyPadding="5px"
2: runat="server" Width="500px" EnableCollapse="True">
3: <Items>
4: <ext:TextBox Label="你所在的省份" ID="TextBox1" runat="server">
5: </ext:TextBox>
6: <ext:Button ID="Button1" EnablePostBack="false" runat="server" Text="從列表中選擇">
7: </ext:Button>
8: </Items>
9: </ext:SimpleForm>
10:
11: <ext:Window ID="Window1" Title="編輯" Popup="false" EnableIFrame="true" runat="server"
12: EnableMaximize="true" EnableResize="true" Target="Parent" OnClose="Window1_Close"
13: IsModal="True" Width="750px" Height="450px">
14: </ext:Window>
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: if (!IsPostBack)
4: {
5: Button1.OnClientClick = Window1.GetSaveStateReference(TextBox1.ClientID)
6: + Window1.GetShowReference("./passvalue_iframe_iframe.aspx");
7: }
8: }
9:
界面效果:
子頁面代碼:
1: <ext:SimpleForm ID="SimpleForm1" ShowBorder="false" ShowHeader="false" Title="SimpleForm"
2: EnableBackgroundColor="true" BodyPadding="5px" runat="server" EnableCollapse="True">
3: <Items>
4: <ext:DropDownList ID="ddlSheng" Label="請選擇省份" ShowRedStar="true" runat="server" AutoPostBack="true"
5: OnSelectedIndexChanged="ddlSheng_SelectedIndexChanged">
6: </ext:DropDownList>
7: </Items>
8: </ext:SimpleForm>
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: if (!IsPostBack)
4: {
5: BindSheng();
6: }
7: }
8:
9: private void BindSheng()
10: {
11: ddlSheng.DataSource = SHENG_JSON;
12: ddlSheng.DataBind();
13:
14: ddlSheng.Items.Insert(0, new ListItem("選擇省份", "-1"));
15: }
16:
17: protected void ddlSheng_SelectedIndexChanged(object sender, EventArgs e)
18: {
19: if (ddlSheng.SelectedValue != "-1")
20: {
21: PageContext.RegisterStartupScript(ActiveWindow.GetWriteBackValueReference(ddlSheng.SelectedValue) + ActiveWindow.GetHideReference());
22: }
23: }
對比前面介紹的四個步驟,我們來看下分別對應的代碼:
- 告訴子窗體,父窗體中可以被作為接受結果數據的表單字段有哪些:Window1.GetSaveStateReference(TextBox1.ClientID)
- 打開子窗體:Window1.GetShowReference("./passvalue_iframe_iframe.aspx")
- 用戶操作后得到結果,將結果保存到父窗體中接受數據的表單字段:ActiveWindow.GetWriteBackValueReference(ddlSheng.SelectedValue)
- 關閉子窗體:ActiveWindow.GetHideReference()
在子窗體的下拉列表中選擇“安徽”后,子窗口關閉並返回父頁面:
注意:GetSaveStateReference 和GetWriteBackValueReference有重載方法,可以一次傳入多個表單字段,參考示例。
回發父頁面
通過前面的學習,我們知道了如何是在子窗體(內嵌IFrame)關閉時回發父頁面。但是對於如下兩個需求,該如何實現呢?
- 不關閉子窗體(內嵌IFrame)的情況下回發父頁面;
- 內嵌IFrame的面板中回發父頁面。
下面通過一個示例來展示如何處理第二種情況,先看下最終的頁面效果:
這個頁面的標簽定義如下:
1: <ext:PageManager ID="PageManager1" runat="server" />
2: 頁面一:parent_simplepostback.aspx
3: <ext:Label ID="labResult" runat="server">
4: </ext:Label>
5: <br />
6: <br />
7: <ext:Panel ID="Panel1" runat="server" EnableBackgroundColor="true" ShowBorder="true"
8: Width="400px" Height="250px" EnableIFrame="true" IFrameUrl="parent_simplepostback2.aspx"
9: ShowHeader="true" Title="面板一">
10: </ext:Panel>
后台代碼:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: if (IsPostBack)
4: {
5: if (Request.Form["__EVENTARGUMENT"] == "param_from_simplepostback2")
6: {
7: Alert.Show("來自子面板IFrame中的事件!");
8: }
9: }
10:
11: labResult.Text = "頁面加載時間:" + DateTime.Now.ToLongTimeString();
12: }
特別注意:和我們通常看到的 if(!IsPostBack) 不同,這里的邏輯是在頁面回發時判斷事件參數是否為 param_from_simplepostback2 (而不管事件來源是哪個控件,實際上從后面的代碼可以明顯看出,這個事件的來源為空,是由子頁面手工觸發的)。
點擊回發父頁面時的界面顯示:
那么,點擊此按鈕的邏輯該如何處理呢?
1: protected void Button1_Click(object sender, EventArgs e)
2: {
3: PageContext.RegisterStartupScript("parent.__doPostBack('','param_from_simplepostback2');");
4: }
其實就是調用父頁面的 __doPostBack 函數,指定此次回發事件的事件源為空,事件參數為 param_from_simplepostback2。
小結
窗體控件在實際項目中使用非常廣泛,特別是和表格控件一起提供的新增、編輯等功能。同時本章還詳細描述了如何實現子窗體和父頁面的傳值問題,在實際項目中也有廣泛的應用。






