Solusi ASP.NET MVC: Themeable View Engine

Dalam ASP.NET MVC, lokasi file-file view (.master, .aspx, dan .ascx) sudah di-define berada di bawah folder Views. Nah, jika kita punya kebutuhan untuk membuat aplikasi yang memiliki theme, dan meletakkan file-file view tadi di bawah folder tertentu misalnya Themes\MyTheme, berarti kita harus secara eksplisit memberikan path terhadap view tersebut pada saat view tersebut dipanggil dalam controller. Nah, agar kita tidak perlu melakukannya secara eksplisit setiap kali memanggil view, kita bisa mengubah behavior default dari WebFormViewEngine agar secara otomatis mencari view dalam lokasi-lokasi yang kita define.

Informasi di mana lokasi-lokasi view bisa ditemukan, bisa didapatkan dari property ViewLocationFormats dan MasterLocationFormat. Di dalam WebFormViewEngine, definisi kedua property di atas adalah sebagai berikut:

            MasterLocationFormats = new[] {

                "~/Views/{1}/{0}.master",

                "~/Views/Shared/{0}.master"

            };

 

            ViewLocationFormats = new[] {

                "~/Views/{1}/{0}.aspx",

                "~/Views/{1}/{0}.ascx",

                "~/Views/Shared/{0}.aspx",

                "~/Views/Shared/{0}.ascx"

            };

Di dalam parent class dari WebFormViewEngine yaitu VirtualPathProviderViewEngine didefinisikan virtual method untuk mencari view dalam format lokasi yang ditentukan di atas. Deklarasi dari method-method virtual tersebut kurang lebih adalah seperti ini.

        public virtual ViewEngineResult FindPartialView(

            ControllerContext controllerContext,

            string partialViewName,

            bool useCache)

        {

        }

 

        public virtual ViewEngineResult FindView(

            ControllerContext controllerContext,

            string viewName,

            string masterName,

            bool useCache)

        {

        }

Nah, ditahap ini kita sudah mendapatkan dua informasi penting untuk membuat solusi berupa themeable view engine. OK, langkah pertama kita perlu mengubah ViewLocationFormat dan MasterLocationFormat menjadi seperti dalam source code berikut ini.

            ViewLocationFormats = new[]

            {

                "~/{0}.aspx",

                "~/{0}.ascx",

                "~/Views/{1}/{0}.aspx",

                "~/Views/{1}/{0}.ascx",

                "~/Views/Shared/{0}.aspx",

                "~/Views/Shared/{0}.ascx",

            };

 

            MasterLocationFormats = new[]

            {

                "~/{0}.master",

                "~/Shared/{0}.master",

                "~/Views/{1}/{0}.master",

                "~/Views/Shared/{0}.master",

            };

Perhatikan bahwa kita menambahkan dua entry di ViewLocationFormats dan satu entry di MasterLocationFormats. Kalau kita lihat format entry-entry baru tersebut seakan-akan kita akan mencari view-view tersebut di root directory. Namun tidak demikian adanya, karena kita akan memberikan informasi full path dari view-view tersebut di method FindPartialView() dan FindView(). Perhatikan definisi dari kedua method tersebut di bawah ini.

        public override ViewEngineResult FindPartialView(ControllerContext controllerContext,

            string partialViewName, bool useCache)

        {

            ViewEngineResult viewResult = null;

 

            var themeablePartialName = FormatViewName(controllerContext, partialViewName);

            viewResult = base.FindPartialView(controllerContext, themeablePartialName, useCache);

            if (viewResult != null && viewResult.View != null)

                return viewResult;

 

            var sharedThemeablePartialName = FormatSharedViewName(partialViewName);

            viewResult = base.FindPartialView(controllerContext, sharedThemeablePartialName, useCache);

            if (viewResult != null && viewResult.View != null)

                return viewResult;

 

            return base.FindPartialView(controllerContext, partialViewName, useCache);

        }

 

        public override ViewEngineResult FindView(ControllerContext controllerContext,

            string viewName, string masterName, bool useCache)

        {

            ViewEngineResult viewResult = null;

 

            var themeableViewName = FormatViewName(controllerContext, viewName);

            var themeableMasterName = string.IsNullOrEmpty(masterName) ? "" : FormatMasterName(masterName);

 

            viewResult = base.FindView(controllerContext, themeableViewName, themeableMasterName, useCache);

            if (viewResult != null && viewResult.View != null)

                return viewResult;

 

            var sharedThemeableViewName = FormatSharedViewName(viewName);

            viewResult = base.FindView(controllerContext, sharedThemeableViewName, themeableMasterName, useCache);

            if (viewResult != null && viewResult.View != null)

                return viewResult;

 

            viewResult = base.FindView(controllerContext, viewName, themeableMasterName, useCache);

            if (viewResult != null && viewResult.View != null)

                return viewResult;

 

            return base.FindView(controllerContext, viewName, masterName, useCache);

        }

Untuk bisa memahami cara kerja kedua method di atas, terlebih dahulu kita mesti mengerti definisi method-method yang diberi warna merah dalam source code di atas. Berikut ini definisi method-method tersebut.

        private static string FormatViewName(ControllerContext controllerContext, string viewName)

        {

            var controllerName = controllerContext.RouteData.GetRequiredString("controller");

 

            return string.Concat("Themes/", Controllers.ControllerBase.Setting.Theme,

                "/Views/", controllerName, "/", viewName);

        }

 

        private static string FormatSharedViewName(string viewName)

        {

            return string.Concat("Themes/", Controllers.ControllerBase.Setting.Theme,

                "/Views/Shared/", viewName);

        }

 

        private static string FormatMasterName(string masterName)

        {

            return string.Concat("Themes/", Controllers.ControllerBase.Setting.Theme,

                "/Views/Shared/", masterName);

        }

Method FormatViewName() akan mengkonstruksi full path dari view-view yang terdapat dalam sebuah theme. Nama theme-nya sendiri disimpan dalam sebuah variable yang diakses melalui object Controllers.ControllerBase.Setting. ControllerBase ini adalah sebuah custom base class dari semua controller kita dimana ia memiliki sebuah publick property berupa Setting. Public property ini berfungsi sebagai tempat untuk menyimpan setting-setting yang diperlukan dalam aplikasi termasuk theme.

OK, cara kerja dari FindPartialView() dan FindView() adalah sebagai berikut. Pertama method tersebut akan mencari view-view di dalam folder Themes. Misalnya nama theme-nya adalah MyTheme, maka method tersebut akan mencari view dalam folder ~/Themes/MyThemes/Views. Ingat bahwa dalam pencarian view, pertama kita mencari view tersebut di bawah subfolder yang namanya sama dengan nama controller-nya. Misalnya nama controller-nya adalah Home berarti lokasi pertama yang dicari adalah ~/Themes/MyTheme/Views/Home. Jika view tersebut tidak terdapat dalam subfolder controller, berikutnya akan dicari dalam subfolder shared: ~/Themes/MyTheme/Views/Shared.

Nah, jika ternyata view-view tersebut tidak ter-define dalam folder Themes, kita akan fallback ke default behavior dari method FindView() dan FindPartialView() dimana pencarian akan dilakukan dalam lokasi-lokasi default. Hal ini memungkinkan kita sebagai theme creator untuk men-suply definisi view apabila memang view tersebut bentuk dan tampilannya khusus hanya ada di theme yang kita provide, namun masih tetap bisa menggunakan view standard untuk view-view yang tidak di customize di theme. Hal ini tentu saja meringankan kerja dari theme designer karena ia tidak perlu men-define semua theme.

Perhatikan contoh pendefinisian view dalam gambar berikut ini.


Dalam lokasi standard untuk View, yaitu di bawah folder Views kita membuat dua view untuk controller About, yaitu Display.aspx dan AboutList.ascx. Nah, dalam salah satu theme yang kita buat, yaitu Plain theme, kita bisa me-redefine tampilan dari view AboutList.ascx, agar bentuknya sesuai dengan designer theme, sementara untuk view Display.aspx designer theme memutuskan untuk menggunakan implementasi default.

OK, that's it! semoga posting ini bermanfaat.

Share this post: | | | |
Published Saturday, May 09, 2009 6:54 AM by Agus Suhanto
Filed under: ,

Comments

No Comments
Powered by Community Server (Commercial Edition), by Telligent Systems