這里的用戶控件指的是繼承自System.Web.UI.UserControl的控件,由於本質上用戶控件屬於Asp.Net,所以可以直接放在form標簽或者ContentPanel控件中。后來FineUI增加了UserControlConnector控件,可以使用戶控件方便地參與頁面布局。本篇文章會詳細描述使用UserControlConnector的用戶控件與直接放在ContentPanel中的用戶控件的區別。
創建一個包含自定義屬性的用戶控件
我們將要創建的用戶控件如下圖所示:
這個用戶控件非常簡單,只包含一個面板和一個文本,ASPX標簽如下所示:
1: <ext:Panel runat="server" ID="Panel1" BodyPadding="5px" Title="用戶控件/面板一">
2: <Items>
3: <ext:Label runat="server" ID="labUserInfo">
4: </ext:Label>
5: </Items>
6: </ext:Panel>
來看下用戶控件的后台代碼:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: LoadData();
4: }
5:
6: private void LoadData()
7: {
8: labUserInfo.Text = String.Format("{0}今年{1}歲,住在{2}。", UserName, UserAge, UserCountry);
9:
10: if (!String.IsNullOrEmpty(Properties))
11: {
12: Panel1.RecoverPropertiesFromJObject(JObject.Parse(Properties));
13: }
14: }
15:
16:
17: private string userName;
18: private int userAge;
19: private string userCountry;
20: private string properties;
21:
22: public string Properties
23: {
24: get { return properties; }
25: set { properties = value; }
26: }
27:
28: public string UserName
29: {
30: get { return userName; }
31: set { userName = value; }
32: }
33:
34: public int UserAge
35: {
36: get { return userAge; }
37: set { userAge = value; }
38: }
39:
40: public string UserCountry
41: {
42: get { return userCountry; }
43: set { userCountry = value; }
44: }
從上面代碼可以看出,我們定義了四個屬性:
- UserName:用戶名
- UserAge:用戶年齡
- UserCountry:用戶所在地區
- Properties:JSON定義的面板屬性列表(比如:{BoxMargin:"0",BoxFlex:1}),可以通過RecoverPropertiesFromJObject來恢復。
在ContentPanel中添加用戶控件
在ContentPanel中使用用戶控件非常簡單,由於ContentPanel本來就是用來放置非FineUI的一個特殊控件,來看下代碼:
1: <ext:ContentPanel runat="server" ID="Panel1" EnableBackgroundColor="true" Width="600px"
2: Height="150px" Title="頁面/面板一(ContentPanel->UserInfoControl)">
3: <uc1:UserInfoControl ID="UserInfoControl1" UserName="陳萍萍" UserAge="20" UserCountry="合肥"
4: runat="server" />
5: </ext:ContentPanel>
顯示效果:
分析下生成的HTML代碼:
1: <div id="Panel1_wrapper">
2: <div id="Panel1_content">
3: <div id="Panel1_UserInfoControl1_Panel1_wrapper"></div>
4: </div>
5: </div>
6:
7: <script>
8: var x2 = new Ext.Panel({
9: id: "Panel1_UserInfoControl1_Panel1",
10: renderTo: "Panel1_UserInfoControl1_Panel1_wrapper",
11: items: [x1],
12: title: "用戶控件/面板一"
13: });
14: var x0 = new Ext.Panel({
15: id: "Panel1",
16: renderTo: "Panel1_wrapper",
17: contentEl: "Panel1_content",
18: title: "頁面/面板一(ContentPanel->UserInfoControl)"
19: });
20: </script>
可以看出,此時用戶控件是直接渲染到 Panel1_UserInfoControl1_Panel1_wrapper 這個DIV中,而這個DIV位於Panel1面板中。
使用UserControlConnector添加用戶控件
1: <ext:Panel runat="server" ID="Panel2" EnableBackgroundColor="true" Width="600px"
2: Height="150px" Title="頁面/面板二(Panel->UserControlConnector->UserInfoControl)">
3: <Items>
4: <ext:UserControlConnector runat="server">
5: <uc1:UserInfoControl ID="UserInfoControl2" UserName="陳萍萍" UserAge="20" UserCountry="合肥"
6: runat="server" />
7: </ext:UserControlConnector>
8: </Items>
9: </ext:Panel>
顯示效果:
可以看到,使用UserControlConnector的顯示效果和放在ContentPanel中的效果一樣。但是兩者有本質的區別,來看下生成的HTML代碼:
1: <div id="Panel2_wrapper"></div>
2:
3: <script>
4: var x5 = new Ext.Panel({
5: id: "Panel2_ctl00_UserInfoControl2_Panel1",
6: items: [x6],
7: title: "用戶控件/面板一"
8: });
9: var x8 = new Ext.Panel({
10: id: "Panel2",
11: renderTo: "Panel2_wrapper",
12: items: [x5],
13: title: "頁面/面板二(Panel->UserControlConnector->UserInfoControl)"
14: });
15: </script>
此時,用戶控件不是直接被渲染到頁面上的DIV中,而是作為Panel2的items屬性(items: [x5]),也就是說用戶控件的渲染是由父控件控制的。
這種本質的區別也正是FineUI所追求的,由於用戶控件的渲染是由父控件控制的,所以可以很方便的參與頁面布局,比如改造上面的例子:
1: <ext:Panel runat="server" ID="Panel3" EnableBackgroundColor="true" Width="600px"
2: Height="150px" Layout="Fit" Title="頁面/面板三(Layout=Fit, Panel->UserControlConnector->UserInfoControl)">
3: <Items>
4: <ext:UserControlConnector runat="server">
5: <uc1:UserInfoControl ID="UserInfoControl3" UserName="陳萍萍" UserAge="20" UserCountry="合肥"
6: runat="server" />
7: </ext:UserControlConnector>
8: </Items>
9: </ext:Panel>
這個示例和上一個例子的唯一區別就是布局的使用(Layout=Fit),生成的界面效果確實完全不同的:
可以看到,用戶控件參與到了頁面布局,此時完全充滿了外部的面板。而這種效果是ContentPanel所無法實現的。
動態添加用戶控件
那么如何通過動態添加用戶控件的方式來實現上一個例子呢:
1: <ext:Panel runat="server" ID="Panel3" EnableBackgroundColor="true" Width="600px"
2: Height="150px" Layout="Fit" Title="頁面/面板三(Layout=Fit, Panel->UserControlConnector->UserInfoControl)">
3: </ext:Panel>
1: protected void Page_Init(object sender, EventArgs e)
2: {
3: UserControlConnector ctrlConnector2 = new UserControlConnector();
4: Panel3.Items.Add(ctrlConnector2);
5: ctrlConnector2.Controls.Add(CreateANewUserControl());
6: }
7:
8: private UserInfoControl CreateANewUserControl()
9: {
10: UserInfoControl ctrl = LoadControl("~/usercontrol/UserInfoControl.ascx") as UserInfoControl;
11: ctrl.UserName = "陳萍萍";
12: ctrl.UserAge = 20;
13: ctrl.UserCountry = "合肥";
14:
15: return ctrl;
16: }
需要注意的有如下幾點:
- 動態添加控件的代碼放在Page_Init中,在前面《表格之動態創建列》中有詳細描述;
- 通過LoadControl來動態創建用戶控件。
用戶控件參與復雜的頁面布局
上個例子使用了Fit布局,下面的例子會使用更加復雜的布局VBox,先看一個簡單的示例:
1: <ext:Panel runat="server" ID="Panel1" EnableBackgroundColor="true" Width="600px"
2: Height="200px" Layout="VBox" BoxConfigAlign="Stretch" BoxConfigPosition="Start"
3: BoxConfigPadding="5" BoxConfigChildMargin="0 0 5 0" Title="頁面/面板一(Layout=VBox, Panel->(UserControlConnector->UserInfoControl,Panel))">
4: <Items>
5: <ext:UserControlConnector ID="UserControlConnector1" runat="server">
6: <uc1:UserInfoControl ID="UserInfoControl1" UserName="陳萍萍" UserAge="20" UserCountry="合肥"
7: runat="server" />
8: </ext:UserControlConnector>
9: <ext:Panel runat="server" ID="Panel3" BodyPadding="5px" BoxFlex="1" BoxMargin="0"
10: Title="頁面/面板二">
11: <Items>
12: <ext:Label runat="server" Text="胡斐今年22歲,住在駐馬店。">
13: </ext:Label>
14: </Items>
15: </ext:Panel>
16: </Items>
17: </ext:Panel>
在這個例子中,Panel1包含兩個子控件,第一個控件是一個用戶控件(由於沒有應用布局屬性,所以用戶控件的高度將是默認的高度),另一個控件使用了布局屬性BoxFlex=1,也就是說占據剩余的全部高度。頁面顯示效果:
為了實現相同的效果,我們也可以使用用戶控件取代上例的第二個子控件,如下所示:
1: <ext:Panel runat="server" ID="Panel2" EnableBackgroundColor="true" Width="600px"
2: Height="200px" Layout="VBox" BoxConfigAlign="Stretch" BoxConfigPosition="Start"
3: BoxConfigPadding="5" BoxConfigChildMargin="0 0 5 0" Title="頁面/面板一">
4: <Items>
5: <ext:UserControlConnector ID="UserControlConnector2" runat="server">
6: <uc1:UserInfoControl ID="UserInfoControl2" UserName="陳萍萍" UserAge="20" UserCountry="合肥"
7: runat="server" />
8: </ext:UserControlConnector>
9: <ext:UserControlConnector ID="UserControlConnector3" runat="server">
10: <uc1:UserInfoControl ID="UserInfoControl3" Properties="{BoxMargin:'0',BoxFlex:1}" UserName="胡斐" UserAge="22" UserCountry="駐馬店"
11: runat="server" />
12: </ext:UserControlConnector>
13: </Items>
14: </ext:Panel>
要特別注意第二個用戶控件的Properties屬性(這個是我們自定義的),這是一個JSON字符串表示的對象,包含兩個屬於面板的屬性BoxMargin和BoxFlex,在前面我們已經看到這兩個屬性被最終應用到用戶控件中的面板上。
其實UserControlConnector可以包含多個子控件,因此上例可以簡化為:
1: <ext:Panel runat="server" ID="Panel4" EnableBackgroundColor="true" Width="600px"
2: Height="200px" Layout="VBox" BoxConfigAlign="Stretch" BoxConfigPosition="Start"
3: BoxConfigPadding="5" BoxConfigChildMargin="0 0 5 0" Title="頁面/面板二">
4: <Items>
5: <ext:UserControlConnector ID="UserControlConnector4" runat="server">
6: <uc1:UserInfoControl ID="UserInfoControl4" UserName="陳萍萍" UserAge="20" UserCountry="合肥"
7: runat="server" />
8: <uc1:UserInfoControl ID="UserInfoControl5" UserName="胡斐" UserAge="22" UserCountry="駐馬店"
9: runat="server" />
10: </ext:UserControlConnector>
11: </Items>
12: </ext:Panel>
此時,對第二個用戶控件Properties屬性的設置是在后台進行的:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: if (!IsPostBack)
4: {
5: LoadData();
6: }
7: }
8:
9: private void LoadData()
10: {
11: JObject jo = new JObject();
12: jo.Add("BoxMargin", "0");
13: jo.Add("BoxFlex", 1);
14: UserInfoControl5.Properties = jo.ToString(Formatting.None);
15: }
小結
用戶控件有時非常實用,特別是對於一些需要在多個地方重用UI邏輯。而FineUI提供的UserControlConnector讓用戶控件可以很方便的參與頁面布局,從而在重用UI邏輯的同時保持頁面的美觀一致。





