比如說我要為Textbox增加一個叫Role的屬性,除了繼承Textbox類別增加屬性之外,我們還可以使用IExtenderProvider來為現有的物件擴充屬性,就像下圖那樣,新增了自己定義的屬性
有了Role屬性之後,我還要驗証登入的帳號是什麼Role,我會利用Thread.CurrentPrincipal屬性來決定角色及規則,若帳號的Role跟控制項的Role相同,該帳號才能使用這個控制項
開始前的準備:
1.我們會需要ProvideProperty Attribute,在類別加入以下定義,其中Role就是我們要擴充的屬性:
[ToolboxItemFilter("System.Windows.Forms"), ProvideProperty("Role", typeof(Control))]
2.類別需要繼承IExtenderProvider介面
3.類別裡需要定義SetRole以及GetRole方法,這樣才能在設計對話框裡看到Role屬性。
4.由GetRole方法回傳的型別決定屬性對話框的樣式,基本上它能依你的屬性來決定樣式,當然你也可以自己定義Attribute,就像下列程式碼是多行字串的樣式:
[DefaultValue(""), Localizable(true), Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
當然囉,特殊的屬性必須要自己定義,比如類別,詳細做法可參考我以前的文章,http://www.dotblogs.com.tw/yc421206/archive/2010/07/04/16351.aspx
接下來就來開始實作:
先建立一個User Control控制項專案Security.Forms
然後把UserControl1.cs改成RoleContainer.cs,並增加IExtenderProvider繼承,當然你也可以把UserControl的繼承改成Component;UserControl會在UI上顯示控制項,Component不會在UI上顯示控制項,我在VS2010找不到Component的範本,只好手動這樣改,先完成初步的外殼
namespace Security.Forms { [ToolboxItemFilter("System.Windows.Forms"), ProvideProperty("Role", typeof(Control))] public partial class RoleContainer : Component, IExtenderProvider { #region 實作 IExtenderProvider 成员 public bool CanExtend(object target) { //排除自己跟Form return ((target is Control) && !(target is RoleContainer) && !(target is Form)); } #endregion #region construct public RoleContainer(IContainer Cont) : this() { if (Cont == null) { throw new ArgumentNullException("cont"); } Cont.Add(this); } public RoleContainer() { } #endregion //TODO:實作Role屬性 } }
TODO裡要做的就是SetRole以及GetRole方法
private List<Role> _RoleControls = new List<Role>(); [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ReadOnlyCollection<Role> RoleControls { get { return new ReadOnlyCollection<Role>(this._RoleControls); } private set { } } public void SetRole(Control Ctrl, RoleType? ControlRole) { if (Ctrl is RoleContainer) { return; } if (ControlRole != null) { Ctrl.Enabled = Thread.CurrentPrincipal.IsInRole(ControlRole.ToString()); } //判斷控制項是否存在 Role role = this.findControl(Ctrl, this._RoleControls); if (role == null) { role = new Role() { Control = Ctrl, RoleType = ControlRole }; this._RoleControls.Add(role); } else { role.RoleType = ControlRole; } } public RoleType? GetRole(Control Ctrl) { Role item = this.findControl(Ctrl, this._RoleControls); if (item != null) { return item.RoleType; } else { return null; } } Role findControl(Control Ctrl, List<Role> Collection) { //判斷控制項是否存在 var query = from data in Collection where data.Control.Name == Ctrl.Name let Index = Collection.IndexOf(data) select new { Index }; int count = query.Count(); Role item = null; if (count > 0) { item = Collection[query.First().Index]; } return item; }
建立Security.Role Class專案
加入RoleType類別
namespace Security.Role { public enum RoleType { Admin = 1, User = 2, PowerUser = 3, } }
加入Identity 類別
namespace Security.Role { public class Identity : IIdentity { public string AuthenticationType { get { return "Custom AuthenticationType"; } } public bool IsAuthenticated { get; internal set; } public string Name { get; private set; } public RoleType RoleType { get; set; } public string Password { get; private set; } public Identity(string Name) : this(Name, "", RoleType.Admin) { } public Identity(string Name, string Password) : this(Name, Password, RoleType.Admin) { } public Identity(string Name, RoleType Role) : this(Name, "", Role) { } public Identity(string Name, string Password, RoleType Role) { this.Name = Name; this.Password = Password; this.RoleType = Role; } } }
加入Principle 類別,在這裡我用了一些假帳號資料DefaultRoles,verifyIdentity方法會去驗証帳號是否正確
namespace Security.Role { public class Principle : IPrincipal { #region 實作IPrincipal 成员 private Identity _Identity; public IIdentity Identity { get { return _Identity; } } //判斷角色是否在規則內 public bool IsInRole(string ControlRole) { RoleType controlRole = (RoleType)Enum.Parse(typeof(RoleType), ControlRole); if (this._Identity == null) { //身份驗證失敗 return false; } else { //身份驗證成功 if (this._Identity.RoleType == controlRole) { //登入角色驗証成功 return true; } else { //登入角色驗証失敗 return false; } } } #endregion public Principle(Identity Identity) { //搜尋帳號是否已建立 this._Identity = verifyIdentity(Identity); if (this._Identity != null) { this._Identity.IsAuthenticated = true; } } //身份驗証 Identity verifyIdentity(Identity Identity) { var query = from data in DefaultRoles where data.Name.ToLower() == Identity.Name.ToLower() let Index = this.DefaultRoles.IndexOf(data) select new { Index }; int count = query.Count(); Identity identity = null; if (count > 0) { identity = this.DefaultRoles[query.First().Index]; return identity; } else { return null; } } public override string ToString() { if (this._Identity == null) return ""; else return string.Format("帳號:{0},角色:{1}", this._Identity.Name, this._Identity.RoleType.ToString()); } //定義內建帳號 private List<Identity> _DefaultRoles; public List<Identity> DefaultRoles { get { if (this._DefaultRoles == null) { this._DefaultRoles = new List<Identity>() { new Identity("admin",RoleType.Admin), new Identity("user",RoleType.User), new Identity("power",RoleType.PowerUser) }; } return _DefaultRoles; } } } }
完成後在Client裡引用它們建立以下控制項
定義控制項的Role屬性
呼叫以下方法決定控制項顯示樣式
void login(string user) { this._Ientity = new Identity(user); this._Principle = new Principle(_Identity); Thread.CurrentPrincipal = _Principle; this.roleContainer1.SwitchRole(); this.label1.Text = this._Principle.ToString(); }
程式載入,有定義Role屬性的控制項,因身份不明被停用
按下power login
按下user login
範例下載