2014年11月18日 星期二

Rendor PartialView dynamically with Builder patten

.NET   MVC    Design Pattern

背景


專案中需要在MVC同一個VIEW上動態依照後端提供的產品規格(View Model)做隱藏/顯示欄位(選項) 本範例使用Builder設計模式來達到此目的。

環境


l   Windows 7 pro
l   IIS Express 7.5
l   Visual Studio 2013 update3 (C#)


實作截圖



l   2 partial view and each contains single data.


l   3 partial views and different data.




實作


Model

l   Parent

public class Parent
{
        [Key]
        [DisplayName("代號")]
        public String Id { get; set; }

        [DisplayName("父母姓名")]
        public String Name { get; set; }
}

l   Child

public class Child
{
        [Key]
        public String Id { get; set; }

        [DisplayName("小孩姓名")]
        public String Name {get;set;}
        [DisplayName("生日")]
        public String Birthday { get; set; }
       
}

l   Pet

public class Pet
{
        [Key]
        public String Id { get; set; }
        [DisplayName("寵物")]
        public String PetType { get; set; }
}

l   MyHome

public class MyHome
{
        public IEnumerable<Model.Parent> Parents { get; set; }
        public IEnumerable< Model.Child> Chidren { get; set; }
        public IEnumerable< Model.Pet> Pets { get; set; }
}


View

l   建立以下View
   
    

l   _ChildPartial.cshtml



@using Chapter01.Domain.Model
@model Child

<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Birthday)
        </th>
       
    </tr>


    @if (Model != null)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => Model.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => Model.Birthday)
            </td>
           
        </tr>
    }
</table>



_ParentPartial & _PetPartial  兩個Partial View 請如上建立。


l   Index.cshtml

主頁面由三個Partial View組成, 且在run-time時由ViewModel的資料來決定顯示與否。

@using Chapter01.Domain.Model
@model Chapter01.Domain.Model.MyHome

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>
<div style="background-color:lightblue">
    @if (Model.Parents != null)
    {
        for (int i = 0; i < Model.Parents.Count(); i++)
        {
            @Html.Partial("_ParentPartial", Model.Parents.ToList()[i])
        }
    }
</div>


<div style="background-color:lightcyan">
    @if (Model.Chidren != null)
    {
        for (int i = 0; i < Model.Chidren.Count(); i++)
        {
            @Html.Partial("_ChildPartial", Model.Chidren.ToList()[i])
        }
    }
</div>

<div style="background-color:lightgreen">
    @if (Model.Pets != null)
    {
        for (int i = 0; i < Model.Pets.Count(); i++)
        {
            @Html.Partial("_PetPartial", Model.Pets.ToList()[i])
        }
    }
</div>



實作Builder

有了View,只要在Controller傳回ViewResult 時塞入model就可以了。 所以我們這邊開始實作Builder的程式碼。

l   Builder

public abstract class HomeBuilder<T>
{
        /// 建立物件
        abstract public T Create();

        abstract protected void CreateParent(T myHome);
        abstract protected void CreateChild(T myHome);
        abstract protected void CreatePet(T myHome);
}

l   Director

public class HomeBuildDirector : IDisposable
{
        private MyHome _myHome;

        public HomeBuildDirector(HomeBuilder<MyHome> homeBuilder)
        {
            this._myHome = homeBuilder.Create();
        }

        public MyHome Get()
        {
            return this._myHome;
        }

        public void Dispose()
        {
            if (this._myHome != null)
            {
                this._myHome = null;
            }
        }
}


l   ConcreteBuilder

1.  ConcreteBuilder 1
public class PeterHome<T> : HomeBuilder<T> where T : MyHome, new()
{
        override public T Create()
        {
            var peterHome = new T();
            this.CreateParent(peterHome);
            this.CreateChild(peterHome);
            this.CreatePet(peterHome);

            return peterHome;
        }

        override protected void CreateParent(T myHome)
        {
            var a1 = new Parent()
            {
                Id = "Peter",
                Name = "Peter Chang"
            };
           
            var setA = new List<Parent>() { a1 };
            myHome.Parents = setA.AsEnumerable();
        }

        override protected void CreateChild(T myHome)
        {
            myHome.Chidren = null;
        }

        override protected void CreatePet(T myHome)
        {
            var c1 = new Pet()
            {
                PetType = "黃金獵犬"
            };
            var setC = new List<Pet>() { c1 };
            myHome.Pets = setC.AsEnumerable();
        }
}

2.  ConcreteBuilder 2

public class AnakinHome<T> : HomeBuilder<T> where T : MyHome, new()
{

        override public T Create()
        {
            var anakinHome = new T();
            this.CreateParent(anakinHome);
            this.CreateChild(anakinHome);
            this.CreatePet(anakinHome);

            return anakinHome;
        }

        override protected void CreateParent(T myHome)
        {
            var a1 = new Parent()
            {
                Id = "Anakin",
                Name = "Anakin Skywalker"
            };
            var a2 = new Parent()
            {
                Id = "Padme",
                Name = "Princess Amidala"
            };
            var setA = new List<Parent>() { a1, a2 };
            myHome.Parents = setA.AsEnumerable();
        }

        override protected void CreateChild(T myHome)
        {
            var b1 = new Child()
            {
                Name = "Luke Skywalker",
                Birthday = "??"
            };
            var b2 = new Child()
            {
                Name = "Leia Skywalker",
                Birthday = "??"
            };

            var setB = new List<Child>() { b1,b2 };
            myHome.Chidren = setB.AsEnumerable();

        }

        override protected void CreatePet(T myHome)
        {
            var c1 = new Pet()
            {
                PetType = "R2D2"
            };
            var c2 = new Pet()
            {
                PetType = "C3PO"
            };

            var setC = new List<Pet>() { c1,c2 };
            myHome.Pets = setC.AsEnumerable();

        }
}


3.  如果有需要,可以建立更多ConcreteBuilder class.


Controller

l   Action

在主程式可決定要用的ConcreteBuilder~

public ActionResult Index()
{
MyHome model = null;
    
//The builder of Anakin's home
    var builder = new Chapter01.Domain.Model.AnakinHome<MyHome>();
//The builder of Peter's home
    //var builder = new Chapter01.Domain.Model.PeterHome<MyHome>();

//Build it!
    using (var builderDirector = new
Chapter01.Domain.Builder.HomeBuildDirector(builder))
    {
        model = builderDirector.Get();
    }
return View(model);
}




Reference




沒有留言:

張貼留言