我是如何實現一個通用的驗證基類的?


驗證的基本流程和基本場景

  • 驗證一個控件,其步驟如下
    • 獲取控件的值
      • 提供默認的控件的取值方法
      • 可以通過注冊控件的方法來注冊獲取某個控件的值
    • 判別要驗證的類型:isNumber,NotNull……
      • 通過綁定到的實體類的特性,我們可以知道具體的類型
      • 在調用方法中指定驗證的類型
    • 調用驗證函數進行驗證
      • 默認的調用函數
      • 通過方法傳遞進來的驗證函數
    • 驗證失敗提示失敗信息
      • 基本的類型有默認的提示:不能為空、必須為數字、必須為日期……
      • 通過方法傳入
      • 提示的方法可以由子類進行重寫,這樣就能根據不同的子類方法,達到實現不同的提示類型
  • 驗證多個控件,其實就是遞歸所有的控件組,調用“一個控件的驗證”
    • 一個Form下的所有控件
    • 某個容器控件(比如PanelTabPage……)下的所有控件

使用方法示例

以下通過一些具體的示例來展示上述驗證類的使用方法

支持DotNetBar的控件以及使用DotNetBar的氣泡控件和高亮控件進行提示

構造函數

private BalloonValidator(Type type)
    : base(type)
{
    Register<TextBoxX>(new[] { "txt" }, t => ((TextBoxX)t).Text)
        .Register<ComboBoxEx>(new[] { "ddl" }, t =>
        {
            var ddl = (ComboBoxEx)t;
            if (ddl.DropDownStyle == ComboBoxStyle.DropDown)
            {
                return ddl.Text;
            }
            return ddl.SelectedIndex > 0 ? "MSG" : string.Empty;
        })
        .Register<CheckBoxX>(new[] { "chk" }, t => ((CheckBoxX)t).CheckState == CheckState.Indeterminate ? string.Empty : "MSG")
        .Register<IntegerInput>(new[] { "ii" }, t => ((IntegerInput)t).Text);
}

public BalloonValidator(Type type, BalloonTip tip)
    : this(type)
{
    _tip = tip;
}

public BalloonValidator(Type type, BalloonTip tip, Highlighter highlighter)
    : this(type, tip)
{
    _highligther = highlighter;
}

重寫錯誤驗證時的提示

public override Validator HandleError(Control ctrl, string errorMsg)
{
    ShowBalloonTip(ctrl, errorMsg);
    if (_highligther != null) _highligther.SetHighlightColor(ctrl, eHighlightColor.Red);

    return this;
}

重新驗證正確時的方法

public override Validator HandleSuccess(Control ctrl)
{
    _tip.Remove(ctrl);
    if (_highligther != null) _highligther.SetHighlightColor(ctrl, eHighlightColor.None);
    return this;
}

驗證使用步驟

在Model中設置特性

[DataType(DataType.Currency, ErrorMessage = "“運費”必須為數字")]
public double StartYunShu
{
    get;
    set;
}

目前支持的特性為:

  • DataType:數據類型
    • DataType.Currency:數值類型
    • DataType.DateDataType.DateTime:日期類型
  • Required:不能為空
  • StringLengthStringLength.Max:字符長度最長不能超過多少
  • RegularExpression:符合某個正則表達式

注意控件名稱的命名規則要有一定的規范

控件命名規則為:[控件前綴] + [Model的屬性名稱]

目前系統的前綴規范為:

  • 文本框TextBox:txt
  • 下拉框ComboBox:ddl
  • 日期控件:DateTimePicker:dtp
  • 復選框:CheckBox:chk
  • 數字輸入控件:IntegerInput:ii

在CheckInput方法中進行驗證

if (!_validator.Verify(Controls)) return false;

實際使用示例

  • 實例化一個驗證類:_validator = new BalloonValidator(typeof(NTier.Entity.CWGL_AccountHis), balloonTip1, highlighter1);

  • 對所有默認的控件進行驗證:if (!_validator.Verify(Controls)) return false;

  • 對控件名稱和實體屬性名稱無法匹配的進行手動驗證:if (!_validator.Verify(txtMoney, "發生金額", Validator.VerifyType.Number)) return false;——實體類NTier.Entity.CWGL_AccountHis沒有類型Money這樣的屬性

  • 對於由多個控件組成的屬性,是無法根據屬性的類型進行驗證的,這個時候需要手動寫驗證函數:if (!_validator.Verify(lblUnSureCost, "請選擇費用是否確定", () => rbtnYes.Checked || rbtnNo.Checked)) return false;

  • 對於復雜的驗證方法,同樣可以通過自定義驗證函數進行驗證:

    if (!((NTier.Entity.CWGL_AccountNew)ddlFNumber1.SelectedItem).FisDetail)
    {
        if (!_validator.Verify(ddlFNumber2, "二級科目", Validator.VerifyType.Required)) return false;
        if (ddlFNumber2.SelectedIndex > 0 &&
            !((NTier.Entity.CWGL_AccountNew)ddlFNumber2.SelectedItem).FisDetail)
        {
            if (!_validator.Verify(ddlFNumber3, "三級科目", Validator.VerifyType.Required)) return false;
        }
    }
    

實現的類的基本方法概覽

注冊控件

///首先定義控件存儲對象
class CtrlObject
{
    public string Key { get; private set; }
    public Type Type { get; private set; }
    public string[] Prefix { get; private set; }
    public Func<Component, object> GetValue { get; private set; }

    public CtrlObject(Type type, string[] prefix, Func<Component, object> getValueHandler)
    {
        if (getValueHandler == null) throw new ArgumentNullException("getValueHandler");

        Type = type;
        Prefix = prefix;
        GetValue = getValueHandler;

        Key = type.FullName;
    }
}

public Validator Register<TCtrl>(string[] prefix, Func<Component, object> getValue, bool cover = false)
{
    var ctrlObj = new CtrlObject(typeof(TCtrl), prefix, getValue);

    if (!_getValueHandler.ContainsKey(ctrlObj.Key)) _getValueHandler.Add(ctrlObj.Key, ctrlObj);
    else if (cover) _getValueHandler[ctrlObj.Key] = ctrlObj;

    return this;
}

在構造函數中注冊默認的控件

private Validator()
{
    Register<TextBox>(new[] { "txt" }, t => ((TextBox)t).Text)
        .Register<ComboBox>(new[] { "ddl" }, t => ((ComboBox)t).SelectedIndex > 0 ? "MSG" : string.Empty)
        .Register<DateTimePicker>(new[] { "dtp" }, t =>
        {
            var date = (DateTimePicker)t;
            if (date.Checked) return date.Value;
            return null;
        });
}

public Validator(Type t)
    : this()
{
    if (t != null)
    {
        foreach (var p in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            if (!_properties.ContainsKey(p.Name)) _properties.Add(p.Name, p);
        }
    }
}

驗證控件

驗證單一控件是否合法

public bool Verify(Control ctrl)
{
    var args = OnBeforeVerify(ctrl);
    return args.Canceled || ValidateInner(ctrl);
}

private bool ValidateInner(Control ctrl, bool triggerAllInvalidControl = true)
{
    if (_properties.Count == 0) throw new Exception("請先設置要驗證的對象類型!");

    //獲取對應的處理器,如果沒有,則默認不加入驗證
    var handler = GetHandler(ctrl);
    if (handler == null) return true;
    var value = handler.GetValue(ctrl);

    var property = GetProperty(handler.Prefix, ctrl);
    if (property == null) return true;//找不到對應的屬性

    var result = ((ValidateRequired(property, value)
                   ?? ValidateDataType(property, value))
                  ?? ValidateStingLength(property, value))
                 ?? ValidateRegularExpression(property, value);

    if (result != null)
    {
        if (triggerAllInvalidControl)
        {
            foreach (var key in _invalidControls.Keys)
            {
                if (ctrl.Name.Equals(key)) continue;
                ValidateInner(_invalidControls[key], false);
            }
        }

        HandleError(ctrl, result.ErrorMessage);
        if (triggerAllInvalidControl && !_invalidControls.ContainsKey(ctrl.Name))
            _invalidControls.Add(ctrl.Name, ctrl);//添加到錯誤列表
    }
    else
    {
        HandleSuccess(ctrl);
        if (triggerAllInvalidControl && _invalidControls.ContainsKey(ctrl.Name))
            _invalidControls.Remove(ctrl.Name);//從錯誤列表中刪除已經驗證成功的控件
    }

    return result == null;
}

驗證某個容器下所有的控件是否合法

/// <summary>
/// 驗證某個控件下的所有子控件
/// </summary>
/// <param name="ctrls">The CTRLS.</param>
/// <returns></returns>
public bool Verify(Control.ControlCollection ctrls)
{
    foreach (Control ctrl in ctrls)
    {
        if (Ignore(ctrl)) continue;

        if (IsContainer(ctrl))
        {
            if (!Verify(ctrl.Controls)) return false;
        }
        else
        {
            if (!Verify(ctrl)) return false;
        }
    }
    return true;
}

驗證是否為某個類型

public bool Verify(Control ctrl, string name, VerifyType type)
{
    var handler = GetHandler(ctrl);
    if (handler == null) throw new Exception(string.Format("找不到類型“{0}”的獲取值的處理器!", ctrl.GetType()));
    var value = handler.GetValue(ctrl);
    if (value == null || string.IsNullOrEmpty(value.ToString()))
    {
        if (type.HasFlag(VerifyType.Required))
        {
            HandleError(ctrl, string.Format("{0}不能為空!", name));
            return false;
        }
    }
    else
    {
        if (type.HasFlag(VerifyType.Number))
        {
            if (!ConvtUtil.GetNullable<double>(value).HasValue)
            {
                HandleError(ctrl, string.Format("{0}必須為數字!", name));
                return false;
            }
        }
    }

    return true;
}

在調用驗證方法時傳入指定的驗證函數

public bool Verify(Control ctrl, string msg, Func<bool> verifyFunc)
{
    var args = OnBeforeVerify(ctrl);
    if (args.Canceled) return true;

    if (!verifyFunc())
    {
        HandleError(ctrl, msg);
        return false;
    }

    HandleSuccess(ctrl);
    return true;
}

驗證成功時或者失敗時的處理

public virtual Validator HandleError(Control ctrl, string errorMsg)
{
    ctrl.Focus();
    MessageBoxHelper.ShowWarning(errorMsg);
    return this;
}

public virtual Validator HandleSuccess(Control ctrl) { return this; }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM