[C#.NET][Winform][User Control] 使用 IExtenderProvider 擴充控制項屬性 並 驗証控制項角色

  • 6405
  • 0
  • 2013-08-16

[C#.NET][Winform][User Control] 使用 IExtenderProvider 擴充控制項屬性 並 驗証控制項角色

比如說我要為Textbox增加一個叫Role的屬性,除了繼承Textbox類別增加屬性之外,我們還可以使用IExtenderProvider來為現有的物件擴充屬性,就像下圖那樣,新增了自己定義的屬性

image

 

有了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

image

 

然後把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裡引用它們建立以下控制項

image

 

定義控制項的Role屬性

image

 

呼叫以下方法決定控制項顯示樣式

 

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屬性的控制項,因身份不明被停用

image

 

按下admin login
image

 

按下power login

image

 

按下user login

image

 


範例下載

 

Security.Form.zip

 

 

 

 

 

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo