灵魂和外表
在Virtual Lab的引擎中,V(irtual)开头的类抽象了Virtual Lab中的对象(例如VObject,VItem),这些对象不能直接在Silverlight的UI线程中绘制到画布上,所以,还需要定义相应的“UI”类,告诉Silverlight如何渲染这些对象。在Virtual Lab引擎中,UI(User Interface)开头的类就是这些对象(UIObject、UIItem),例如UIObject类告诉了Silverlight如何把一个VObject对象“画”到程序中。我使用以下UML图来描述二者的关系,这就是桥接模式:
使用桥接模式,能够明确地划分抽象与实现,类的结构也更为清晰。由于桥接模式分离了抽象和实现,使得这两个部分能够分别的演化而不必修改另外一部分的内容。使程序可以在实现部分定义一些基本的原子方法,而在抽象部分则通过组合定义在实现层次中的原子方法来实现系统的功能,通过聚合机制来为对象动态的添加职责,解决了在子类继承中容易引起的子类爆炸的问题。桥接模式更可以提供在各个不同的实现中动态的进行切换,而不必重新编译程序。同时,UI类的使用可以避免重新实现前台模块中已经包含的方法,使得前台各个模块的交叉开发变得极为迅速。
我喜欢称VObject为灵魂,UIObject为外表,当灵魂变换时,对应的改变将在外表上反映出来。
绑定到属性
UIObject从Canvas继承,将“灵魂”设置为“外表”的DataContext后,我用一个BindingSource方法来将VObject中的属性绑定在Canvas的属性上:
protected VObject m_VObject; public VObject VObject { get { return m_VObject; } set { m_VObject = value; this.DataContext = VObject; BindingSource(); } }
BindingSource定义如下:
protected virtual void BindingSource() { this.SetBinding(WidthProperty, new Binding() { Path = new PropertyPath("Width"), Mode = BindingMode.TwoWay }); this.SetBinding(HeightProperty, new Binding() { Path = new PropertyPath("Height"), Mode = BindingMode.TwoWay }); this.SetBinding(Canvas.LeftProperty, new Binding() { Path = new PropertyPath("TopX"), Mode = BindingMode.TwoWay }); this.SetBinding(Canvas.TopProperty, new Binding() { Path = new PropertyPath("TopY"), Mode = BindingMode.TwoWay }); //其他不能绑定的属性,读取配置文件 LoadConfigureFile(); VObject.PropertyChanged += new PropertyChangedEventHandler(OnSourcePropertyChanged); }
Silverlight的绑定,只能作用在DependcyObject类型的属性上,对于VObject中无法绑定的属性,我通过监听VObject的PropertyChanged事件来监视变动,例如当“灵魂”配置文件改变时,通过LoadConfigureFile方法来改变“表现”:
protected virtual void OnSourcePropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "ConfigFile") { LoadConfigureFile(); } }
读取配置文件
Silverlight允许使用WebClient读取服务器文件:
protected virtual void LoadConfigureFile() { if (!String.IsNullOrEmpty(VObject.ConfigFile)) { WebClient fileLoader = new WebClient(); fileLoader.DownloadStringCompleted += new DownloadStringCompletedEventHandler(OnConfigureFileDownload); if (VObject.ConfigFile.ToLower().StartsWith("http://")) { fileLoader.DownloadStringAsync(new Uri(VObject.ConfigFile)); } else { fileLoader.DownloadStringAsync(new Uri( Application.Current.Host.Source, VObject.ConfigFile)); } } }
OnConfigureFileDownload是一个抽象函数,子类通过实现该函数来解析配置文件的内容:
protected abstract void OnConfigureFileDownload(object sender, DownloadStringCompletedEventArgs e);