[Silverlight]使用MVVM模式打造英汉词典

最近比较关注MVVM(Model-View_ViewModel)模式,该模式十分适合WPF/Silverlight的开发。出于练习的目的打算使用Silverlight做个英汉词典(可能是由于近来疯狂的听VOA的缘故),下面针对该项目进行简单的分析。
注:由于Silverlight不支持Command 所以并无法像WPF那样完全实现MVVM模式。

老规矩,先看下运行时的截图
image

这里说下我的开发环境

windows server 2008
visual studio 2008 with sp1
sliverlight 2

下面开始一步一步的制作该应用。

首先新建一个Silverlight项目并宿主在ASP.NET Web Application中(当然我强烈建议宿主在ASP.NET MVC中,不过该项目基本上和宿主端没什么联系而并不是每个人都安装了ASP.NET MVC,所以宿主在可以使用Silverlight控件的Web Form中也许受众面更广泛一些)

在Silverlight的项目中分别新建三个文件夹Model、View、ViewModel并添加相应的文件,最终的解决方案视图如下

image

Model/DictModel.cs以及Model/SentModel.cs为纯粹的业务模型,所有的属性都必须实现IPropertyChanged接口以便在其值更改时可以同时更新UI。这两个类同时继承PropertyChangedBase,该基类很简单,请见我的另外一篇文章:让INotifyPropertyChanged的实现更优雅一些

这两个类的代码如下

DictModel.cs

using System;

namespace EternalDict.Model
{
    public class DictModel : PropertyChangedBase
    {
        string _key;
        public string Key
        {
            get
            {
                return _key;
            }
            set
            {
                _key = value;
                this.NotifyPropertyChanged(p => p.Key);
            }
        }

        string _lang;
        public string Lang
        {
            get
            {
                return _lang;
            }
            set
            {
                _lang = value;
                this.NotifyPropertyChanged(p => p.Lang);
            }
        }

        string _audio;
        public string Audio
        {
            get
            {
                return _audio;
            }
            set
            {
                _audio = value;
                this.NotifyPropertyChanged(p => p.Audio);
            }
        }

        string _pron;
        public string Pron
        {
            get
            {
                return _pron == null ? string.Empty : string.Format("[{0}]", _pron);
            }
            set
            {
                _pron = value;
                this.NotifyPropertyChanged(p => p.Pron);
            }
        }

        string _def;
        public string Def
        {
            get
            {
                return _def;
            }
            set
            {
                _def = value;
                this.NotifyPropertyChanged(p => p.Def);
            }
        }

        System.Collections.ObjectModel.ObservableCollection<SentModel> _sentCollection;
        public System.Collections.ObjectModel.ObservableCollection<SentModel> SentCollection
        {
            get
            {
                return _sentCollection;
            }
            set
            {
                _sentCollection = value;
                this.NotifyPropertyChanged(p => p.SentCollection);
            }
        }

    }
}

SentModel.cs

using System;

namespace EternalDict.Model
{
    public class SentModel : PropertyChangedBase
    {
        string _orig;
        public string Orig
        {
            get
            {
                return _orig;
            }
            set
            {
                _orig = value;
                this.NotifyPropertyChanged(p => p.Orig);
            }
        }

        string _trans;
        public string Trans
        {
            get
            {
                return _trans;
            }
            set
            {
                _trans = value;
                this.NotifyPropertyChanged(p => p.Trans);
            }
        }
    }
}

ViewModel/DictViewModel.cs则用来为View/DictView.xmal提供数据

这里我使用海词http://dict.cn/提供的API,通过Linq to XML进行解析。不过有个问题,海词的API可以提供GBK和UTF8这两种编码的服务,不过当前UTF8并不提供汉英翻译的功能,而silverlight并不支持GBK的编码转换,所以也只能实现英汉查找。也许某天你会发现汉英查找可用了,那么八成是海词官方升级了API。

该类的代码如下

using System;
using System.Net;
using System.Xml.Linq;
using System.Linq;
using System.Text;
using EternalDict.Model;

namespace EternalDict.ViewModel
{
    public class DictViewModel
    {
        string _wordToQuery;
        public DictModel Dm { get { return _dm; } set { _dm = value; } }
        private DictModel _dm;

        public DictViewModel()
        {
            _dm = new DictModel();
        }

        public void QueryWord(string wordToQuery)
        {
            this._wordToQuery = wordToQuery;
            string apiUrlString = string.Format("http://api.dict.cn/ws.php?utf8=true&q={0}", this._wordToQuery);
            Uri endPoint = new Uri(apiUrlString);
            WebClient client = new WebClient();
            client.DownloadStringAsync(endPoint);
            client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
        }

        void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                this.ParseXml(e.Result);
            }
        }

        void ParseXml(string stringToParse)
        {
            /*Silverlight不支持其他编码 囧
            Encoding gbk = Encoding.GetEncoding("GBK");
            Encoding utf8 = Encoding.UTF8;
            byte[] gbkBytes = gbk.GetBytes(stringToParse);
            byte[] utf8Bytes = Encoding.Convert(gbk, utf8, gbkBytes);
            char[] utf8Chars = new char[utf8.GetCharCount(utf8Bytes, 0, utf8Bytes.Length)];
            utf8.GetChars(utf8Bytes, 0, utf8Bytes.Length, utf8Chars, 0);
            stringToParse = new string(utf8Chars);
            */

            XDocument xDoc = XDocument.Parse(stringToParse);
            var nodeDict = xDoc.Root;
            var audio = nodeDict.Elements().Where(p => p.Name == "audio").SingleOrDefault();
            var lang = nodeDict.Elements().Where(p => p.Name == "lang").SingleOrDefault();
            var pron = nodeDict.Elements().Where(p => p.Name == "pron").SingleOrDefault();
            var def = nodeDict.Element("def").Value;
            _dm.Audio = audio == null ? string.Empty : audio.Value;
            _dm.Def = def.Equals("Not Found") ? "未找到该单词的释义" : def;
            _dm.Key = this._wordToQuery;
            _dm.Lang = lang == null ? string.Empty : lang.Value;
            _dm.Pron = pron == null ? "无" : pron.Value;
            var eleSents = nodeDict.Elements().Where(p => p.Name == "sent");
            if (eleSents != null)
            {
                _dm.SentCollection = new System.Collections.ObjectModel.ObservableCollection<SentModel>();
                foreach (var item in eleSents)
                {
                    SentModel sm = new SentModel();
                    sm.Orig = item.Element("orig").Value;
                    sm.Trans = item.Element("trans").Value;
                    _dm.SentCollection.Add(sm);
                }
            }
        }
    }
}

这里公开了DictModel这个属性,用于为View提供数据。在View中完全通过数据绑定与其通讯。现在看下DictView.cs中最重要的几行代码

        DictViewModel dvm = new DictViewModel();
        public DictView()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(Dict_Loaded);
        }
        
        void Dict_Loaded(object sender, RoutedEventArgs e)
        {
            this.DataContext = dvm;
        }

在View加载后便将ViewModel本身作为其DataContext。当点击查询按钮的时候便调用DictViewModel的QueryWord方法。

        private void btnLookUp_Click(object sender, RoutedEventArgs e)
        {
            dvm.QueryWord(txtWord.Text.Trim());
        }

剩下的就是在DictView.xaml中进行UI的设计了,代码比较多就不贴了。比较关键的是

<StackPanel DataContext="{Binding Dm}" Orientation="Vertical" >

这里将BM绑定到该StackPanel的DataContext上,其可视化树上的所有子孙元素便都可以通过Binding与Model进行关联了。

源码下载:点此下载
在线体验:http://024hi.com/lab/ria/dict.html

posted @ 2009-06-14 19:48  紫色永恒  阅读(2883)  评论(19编辑  收藏  举报