Silverlight有個深得我心的設計--TextBox等輸入元素已內建了驗證失敗顯示。

如上圖,當輸入結果不符合預先指定的檢核邏輯時,TextBox會出現紅框、右上角還多個小三角形,點擊後會以動畫浮出檢核失敗的說明訊息(配置時,TextBox右側最好保留一些空白,以免訊息被裁掉;若右側完全無空間,失敗說明會出現在左側),頗為直覺簡潔。

TextBox要如何知道檢核規則? Silverlight巧妙地把它跟Binding(有人翻繫結,有人說綁定,我還是習慣直接說Binding。[參考資料])結合在一起。當TextBox Bind到某個物件的某個屬性上,若設定屬性時發生Exception,TextBox就判定為檢核失敗,並將錯誤訊息顯現出來。依此要領,要自訂檢核邏輯很簡單,在Property的set { ... }時加上檢核程式碼,不符規定時throw new ValidationException(message)就搞定了!

Silverlight 3更多了一個好東西! 一些基本檢核如必填欄位、數值範圍、字串長度限制、Regular Expression比對... 等等,更是只要在Bind對象類別的Property上加註Data Annotation(資料附註)就好,連程式都不用寫。

我將上述應用整理了一個例子(主要是給自己備忘,大家加減看),識別代碼欄位利用資料附註限定必填、字串長度3-5碼,統一編號欄位則利用自訂邏輯檢核8碼數字及檢查號碼。

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Text.RegularExpressions;
 
namespace SLInterop
{
    public class DataBindClass : INotifyPropertyChanged
    {
        #region 統一編號,示範自訂檢核
        private string companyId;
        public string CompanyId  {
            get { return companyId; }
            set
            {
                companyId = value;
                string chkMsg = 
                    ValidateHelper.ValidateCompanyId(value);
                if (chkMsg.Length > 0)
                {
                    //若無效時強迫清空可加入以下兩行
     companyId = "";
                    NotifyPropertyChanged("CompanyId");
                    //抛出錯誤訊息
                    throw new ValidationException(chkMsg);
                }
                NotifyPropertyChanged("CompanyId");
            }
        }
        #endregion
 
        #region 識別碼,示範Data Annontation
        //利用宣告加上檢核條件
        private string id;
        [Required(ErrorMessage="識別碼不可空白!")]
        [StringLength(5, MinimumLength=3, 
            ErrorMessage="識別碼需為3-5碼!")]
        public string Id
        {
            get { return id; }
            set
            {
                id = value;
                //呼叫Validator.ValidateProperty進行檢核
                Validate(value, "Id");
                NotifyPropertyChanged("Id");
            }
        }
        #endregion
 
        #region Annotation 檢核共用函數
        //REF: http://msdn.microsoft.com/en-us/magazine/ee335695.aspx
        protected void Validate(object value, string propertyName)
        {
            Validator.ValidateProperty(value,
            new ValidationContext(this, null, null)
            {
                MemberName = propertyName
            });
        }
        #endregion
 
        #region 屬性值改變通知共用函數,TwoWay Binding必備
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion
    }
    #region 統編檢查
    public static class ValidateHelper 
    {
        static byte[] companyIdChkFac = { 1, 2, 1, 2, 1, 2, 4, 1 };
        public static string ValidateCompanyId(string id)
        {
            //REF: http://herolin.mine.nu/entry/is-valid-TW-company-ID
            //檢核是否為有效統編
            string errMsg = "";
            if (string.IsNullOrEmpty(id)) return errMsg;
            if (!Regex.IsMatch(id, "\\d{8}"))
                errMsg = "統編應為8位數字!";
            else
            {
                int sum = 0;
                for (int i = 0; i < 8; i++)
                {
                    int d = (id[i] - 48) * companyIdChkFac[i];
                    sum += d / 10 + d % 10;
                }
                if (
                    !(sum % 10 == 0 ||
                      id[6] == '7' && (sum + 1) % 10 == 0)
                    )
                    errMsg = "統編檢查碼不符!";
            }
            return errMsg;
        }
    }
    #endregion
 
    public partial class MainPage : UserControl
    {
        //設定TextBox TwoWay Binding
        private void SetTextBoxBinding(
            TextBox txt, object bindTarget, string propName)
        {
            Binding bind = new Binding();
            bind.Mode = BindingMode.TwoWay;
            bind.Path = new PropertyPath(propName);
            bind.Source = bindTarget;
            bind.NotifyOnValidationError = true;
            bind.ValidatesOnExceptions = true;
            txt.SetBinding(TextBox.TextProperty, bind);
        }
 
        public MainPage()
        {
            InitializeComponent();
 
            DataBindClass bdc = new DataBindClass();
            SetTextBoxBinding(txtCmpId, bdc, "CompanyId");
            SetTextBoxBinding(txtId, bdc, "Id");
        }
 
    }
}

簡單說明程式重點:

  1. DataBindClass要實作INotifyPropertyChanged,當資料有變時呼叫PropertyChanged事件,通知Binding的元素反應變動。
  2. CompanyId是在set時加入自訂的檢核邏輯,Id則是用了Required, StringLength資料附註設定檢核。
  3. 自訂檢核失敗時可throw new ValidationException(messageToShow),用資料附註則要在每次設定值時呼叫Validator.ValidateProperty執行檢核。
  4. 檢核統編的做法大致上為1至8位數分別乘上1,2,1,2,1,2,4,1,將八個相乘結果的十位數與個位數相加在一起,除10除得盡就OK,若除不盡餘9且第7位是7的話,也算過關。
  5. SetTextBoxBinding示範如何透用程式設定Binding,而非在XAML中宣告。在我的應用中,較多的機會是由程式動態設定Binding,能寫死在XAML的機會很少。

Comments

# by WizardWu

F# for Silverlight 4 available 已實踐在 .NET / VS 2010 的 F# 函數式編程: http://www.cnblogs.com/alamiye010/archive/2010/05/23/1742084.html .NET 4 来了,带来了超棒般艺术式并行编程解决方案,包括如 TPL (任务并行库 (Task Parallel Library))、 PLINQ (并行语言集成查询(PLINQ,Parallel Language Integrated Query)以及 F#。在目前的 Silverlight 4 版本中为了框架精简,暂时还无法用到 TPL 和 PLINQ (WPF 4 能完美支持),F# 成为 Silverlight 上目前唯一实现并行编程的方式。 对 F# 在 Silverlight 中应用感兴趣的朋友们我强烈推荐 Brian 的博客(微软 F# 开发团队成员)。其中一篇文章提到 F# 在 Silverlight 中的地位问题,有这么一句对我启发很大:标准/推荐的 F# 在 Silverlight 中使用的做法是通过创建例如 C# 的 Silverlight 应用程序后,再引用 F# 编写的 Silverlight 类库。当然,我也能通过hack的方式利用纯 F# 代码编写完整的 Silverlight 应用程序而无须任何的例如 C#、XAML 等等。 此时有朋友肯定会问:F# 在将来的某天是否会革掉 C# 的命而真正成为 .NET 平台中的顶峰语言? 其实非也。从定义看,大家可以先了解下什么是 F#:F# 是微软唯一的 FP 语言 (函数式编程(FP,Functional Programming))。那么相对于函数型程序设计语言,C# 则是标准的面向对象的高级程序设计语言,虽然它们在 .NET 框架下能实现相同的功能,达到一模一样的目的;然而由于两者的性质截然不同,从一者向另一者的转变(包括思维,习惯,编码方式等),特别是如若想两者都精通那真绝非易事;当然,其实最重要的关键点在于两者在 .NET 平台中的地位是并列的,官方有这么一句话点明了它们的关系及未来:F# 也许终将成为程序核心部分设计的首选,而 C# 与 VB 等将在用户界面交互设计方面继续发挥其强大的潜力。 经我这么一说大家是否有种豁然开朗的感觉,没错了,对于精通 C# 的 .NET 开发者来说,我们只需大致了解F#的基本语法,然后由此再进一步掌握F#的并行计算实现的相关代码即可。F#的关键在于异步与并行计算,Brian在博客中也有写到使用F#的九个理由其中就有包含这两点。另一方面,并行计算也并非永远都比串行计算要快。金老师的这篇文章讲得非常详细,同时也举了非常有说服力的例子,小结中有提到:由于“并行”需要付出代价,因此,不是所有的程序都需要转换为并行的,当要处理的数据量很大,或者要执行的数据处理任务繁重,并且这些任务本身就可以分解为互不相关的子任务时,使用并行计算是合适的。 .NET 4.0 并行计算技术基础(3)(金老師的文章) http://blog.csdn.net/bitfan/archive/2009/09/15/4553738.aspx F# for Silverlight 4 available http://lorgonblog.spaces.live.com/blog/ 打个比方,在 Silverlight 游戏开发中,我们完全可以使用 F# 并行计算方式实现 A* 以及游戏中的障碍物数组的动态更新等大数据量的繁杂处理,然后将结果交给 C#,由 C# 去处理数据与界面间的协调问题,从而引导精灵们寻路移动等操作。 意味深长呀,任何语言都需要时间的考验,而我始终坚信:F# 在未来并行运算的世界里终将大放异彩,而 C# 则是 .NET 平台中永不褪色的经典。C# 优异的界面处理能力配合上 F# 强大的并行计算特性,将使得 Silverlight 在未来的 RIA 界独领风骚,这才是 Silverlight 超越其他所有 RIA 技术的最大特性:一流的开发效率和产品性能! ---------------------------------------------------------- 蔡學鏞: F#:微軟的下一代重量級語言 2008 年 6 月 11 日 星期三 http://jerrylovesrebol.blogspot.com/search/label/F%23 http://jerrylovesrebol.blogspot.com/search/label/FP ---------------------------------------------------------- Silverlight 5 Wishlist 02-19-2010 8:59 AM http://forums.silverlight.net/forums/p/163134/395797.aspx Silverlight 在游戏开发方面拥有它独到之处,目前最新版本为 4.0,开发环境集成于强大的 Visual Studio 2010 中。Silverlight 在短短的数年时间里能成长得如此迅速与微软的强力支持分不开,从 Silverlight 5 Wishlist 字里行间中我再次感受到世界对 Silverlight 未来的强烈期待,WEB-3D 全方位支持或将在该版本中绚丽登场,这或许会成为一场空前革命,翘首以待。 ---------------------------------------------------------- 老赵点滴 - 追求编程之美 编程语言的发展趋势及未来方向:函数式编程 http://www.cnblogs.com/JeffreyZhao/tag/F%23/ http://www.cnblogs.com/JeffreyZhao/tag/%e5%87%bd%e6%95%b0%e5%bc%8f%e7%bc%96%e7%a8%8b/ http://www.cnblogs.com/JeffreyZhao http://blog.zhaojie.me/

Post a comment