Created
January 2, 2014 14:58
-
-
Save kocubinski/8220449 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ## Theme and UserControl Inversion of Control | |
// | |
// BaseClasses or Interfaces which UserControls derive from are bound to an implemenation for a given theme. | |
// This gives complete flexibility over which programs are using which controls, and | |
// a container to tie it all together rather than just ConfigKeys | |
// and 'if..then' statements sprinkled throughout the codebase. Pages query ThemeService (see *Resolve* method) | |
// for which UserControl to render, and based on the current program a path to the proper UserControl is returned. | |
// | |
// In this case ThemeService is our IOC container, and the bindings are defined below in C#. | |
// Currently there are two themes ("Default" or "Facelift"), and are put in a static hash-table. When *Resolve* is | |
// called the ConfigurationKey 'Theme' is retrieved, and the proper control returned based on what the ConfigValue is. | |
// | |
// This system could (should?) also use a table of the structure: | |
// | |
// | Id | ProgramId | Type | Binding | | |
// |----+-----------+------------------+----------------------| | |
// | 1 | 2 | LoginControlBase | /UC/LoginThemed.ascx | | |
// | 2 | 2 | IMenuControl | /UC/NavMenu.ascx | | |
// | |
// Or replace ProgramId for ThemeId, etc. | |
using System; | |
using System.Linq; | |
using System.Collections.Generic; | |
using System.Web.UI; | |
using DIMC.TotalPro.Business; | |
public class ThemeControl | |
{ | |
public string Path { get; private set; } | |
public Type Type { get; private set; } | |
public ThemeControl(string path) | |
{ | |
Path = path; | |
} | |
public ThemeControl(Type type) | |
{ | |
Type = type; | |
} | |
} | |
public class ThemeService | |
{ | |
// set to false to disable cache. | |
private static bool UseCache = true; | |
private static readonly IDictionary<string, IDictionary<Type, ThemeControl>> Bindings; | |
private static readonly IDictionary<string, ThemeControl> ControlCache = new Dictionary<string, ThemeControl>(); | |
private readonly int _programId; | |
static ThemeService() | |
{ | |
Bindings = new Dictionary<string, IDictionary<Type, ThemeControl>>(); | |
// Default Theme: | |
Bindings["Default"] = new Dictionary<Type, ThemeControl>() | |
{ | |
{typeof (ILoginControl), new ThemeControl("/UC/Login.ascx")}, | |
{typeof(IMainGreeting), new ThemeControl("/UC/Greeting.ascx")}, | |
{typeof (IFormGreeting), new ThemeControl("/UC/FormGreeting.ascx")}, | |
{typeof (INavigationControl), new ThemeControl("/UC/DefaultNavigation.ascx")}, | |
{typeof (ISiteHeader), new ThemeControl("/UC/Header.ascx")}, | |
{typeof (IAdminHeader), new ThemeControl("/Admin/Header.ascx")}, | |
{typeof (ICreateAccountControl), new ThemeControl("/Login/CreateAccount.ascx")}, | |
{typeof (IForgotPasswordControl), new ThemeControl("/Login/ForgotPassword.ascx")} | |
}; | |
// "Facelift" Theme: | |
Bindings["Facelift"] = new Dictionary<Type, ThemeControl> | |
{ | |
{typeof (ILoginControl), new ThemeControl("/UC/Facelift/Login.ascx")}, | |
{typeof (IMainGreeting), new ThemeControl("/UC/Facelift/Greeting.ascx")}, | |
{typeof (IFormGreeting), new ThemeControl("/UC/Facelift/FormGreeting.ascx")}, | |
{typeof (INavigationControl), new ThemeControl("/UC/Facelift/NavigationMenu.ascx")}, | |
{typeof (IButton), new ThemeControl(typeof(SiteButton))}, | |
{typeof (ISiteHeader), new ThemeControl("/UC/Facelift/HeaderBar.ascx")}, | |
{typeof (IAdminHeader), new ThemeControl("/UC/Facelift/AdminHeader.ascx")}, | |
{typeof (ICreateAccountControl), new ThemeControl("/UC/Facelift/CreateAccount.ascx")}, | |
{typeof (IForgotPasswordControl), new ThemeControl("/UC/Facelift/ForgotPassword.ascx")} | |
}; | |
// Add additional themes here, using whichever UserControls you desire | |
} | |
public ThemeService(int programId) | |
{ | |
_programId = programId; | |
} | |
private static string CheckConfigKeys(SiteConfigurator config, Type type) | |
{ | |
// give these config keys precedence for backwards compatibility.. | |
if (typeof (IMainGreeting).IsAssignableFrom(type)) | |
{ | |
var val = config.Get(ConfigurationKeys.GreetingControl, false); | |
if (val != null) return "/UC/" + val; | |
} | |
return null; | |
} | |
private static ThemeControl ResolveByInterface(Type type, IDictionary<Type, ThemeControl> theme) | |
{ | |
var themeControl = | |
type.GetInterfaces() | |
.Select(i => | |
{ | |
ThemeControl res; | |
return theme.TryGetValue(i, out res) ? res : null; | |
}) | |
.SingleOrDefault(i => i != null); | |
return themeControl; | |
} | |
private static ThemeControl ResolveBySuperClass(Type type, IDictionary<Type, ThemeControl> theme) | |
{ | |
ThemeControl themeControl; | |
var baseType = type.BaseType; | |
if (baseType == null) | |
return null; | |
return !theme.TryGetValue(baseType, out themeControl) | |
? ResolveBySuperClass(baseType, theme) | |
: themeControl; | |
} | |
public static ThemeControl GetControl(int programId, Type cType) | |
{ | |
var config = new SiteConfigurator(programId); | |
// certain controls should always be bound via config keys for backwards compatibility | |
var configControl = CheckConfigKeys(config, cType); | |
if (configControl != null) | |
{ | |
var thControl = new ThemeControl(configControl); | |
return thControl; | |
} | |
// first, try to resolve from cache | |
string themeName = config.Get(ConfigurationKeys.TotalPro_Theme); | |
var cacheKey = string.Format("{0}_{1}", themeName, cType.Name); | |
ThemeControl cached; | |
if (UseCache && ControlCache.TryGetValue(cacheKey, out cached)) | |
return cached; | |
// finally we try to resolve the control by the current theme. | |
var theme = Bindings[themeName]; | |
ThemeControl themeControl = ResolveByInterface(cType, theme) ?? ResolveBySuperClass(cType, theme); | |
if (UseCache) ControlCache[cacheKey] = themeControl; | |
return themeControl; | |
} | |
} | |
public class DynamicControl : BaseControl | |
{ | |
public event Action<Control> ControlCreated; | |
protected DynamicControl() | |
{ | |
Init += OnInit; | |
} | |
private void OnInit(object sender, EventArgs e) | |
{ | |
ThemeControl themeControl = ThemeService.GetControl(ProgramID, GetType()); | |
if (themeControl == null) | |
return; | |
var control = !string.IsNullOrEmpty(themeControl.Path) | |
? Page.LoadControl(themeControl.Path) | |
: (Control) Activator.CreateInstance(themeControl.Type, this); | |
Controls.Add(control); | |
if (ControlCreated != null) | |
ControlCreated(control); | |
} | |
} | |
// below are some interfaces which our dynamic controls implement | |
public interface INavigationControl { } | |
public interface ISiteHeader { } | |
public interface IAdminHeader { } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment