• [原创]FineUI秘密花园(二十九) — 用户控件概述


    这里的用户控件指的是继承自System.Web.UI.UserControl的控件,由于本质上用户控件属于Asp.Net,所以可以直接放在form标签或者ContentPanel控件中。后来FineUI增加了UserControlConnector控件,可以使用户控件方便地参与页面布局。本篇文章会详细描述使用UserControlConnector的用户控件与直接放在ContentPanel中的用户控件的区别。

    创建一个包含自定义属性的用户控件

    我们将要创建的用户控件如下图所示:

    image

    这个用户控件非常简单,只包含一个面板和一个文本,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>

    显示效果:

    image

    分析下生成的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>

    显示效果:

    image

    可以看到,使用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),生成的界面效果确实完全不同的:

    image

    可以看到,用户控件参与到了页面布局,此时完全充满了外部的面板。而这种效果是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,也就是说占据剩余的全部高度。页面显示效果:

    image

    为了实现相同的效果,我们也可以使用用户控件取代上例的第二个子控件,如下所示:

       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逻辑的同时保持页面的美观一致。

    注:《FineUI秘密花园》系列文章由三生石上原创,博客园首发,转载请注明出处。文章目录 官方论坛

  • 相关阅读:
    Unity Ply网格读取
    LoadLibrary加载dll失败
    Anaconda引起cuda MSB3721 with return error code 1
    STL 如何对STL进行扩展,简单小结
    集群环境准备(Centos7)
    MySQL中的常见函数
    Mysql优化_第十三篇(HashJoin篇)
    docker创建和使用mysql
    JNI相关笔记 [TOC]
    选择排序
  • 原文地址:https://www.cnblogs.com/sanshi/p/2848555.html
Copyright © 2020-2023  润新知