本文将继承UIObject,实现背景的渲染,包括读取图片。
和VPlace相应,我用一个继承关系来实现虚拟实验室引擎的渲染类,其中,UIPlace对应的是背景,而UIItem对应的是前景,本章将讨论如何实现UIPlace:
Xml文件
如前文所述,在引擎的设计过程中,我使用两个Xml文件来装载引擎对象的数据:
一个Xml文件(属性文件)包括该对象的特殊属性(例如大小、引擎中的每一个对象都有不同的大小),供VObject使用,VObject对象通过Parse方法从该Xml中获取数据;
另一个Xml文件(配置文件)则表示该对象的显示属性(例如该对象所用的素材,举例说明,一个场景中可能有两棵树,这两棵树所用的图片都是一样的,因此我把这两棵树使用的图片表示在这个Xml文件中),有UIObject使用。UIObject通过LoadConfigFile方法来获取该Xml文件中的数据。
属性文件
和Place有关的属性文件如下:
<place name='NBA' width='1800' height='1200' centerX='0' centerY='0' x='-800' y='-500' file='engine1/place1/place.xml' />
配置文件
和Place有关的配置文件如下:
<?xml version="1.0" encoding="utf-8"?> <place width="100" height="100"> <mappart left="0" top="0" image="engine1/place1/0-0.jpg" /> <mappart left="100" top="0" image="engine1/place1/0-1.jpg" /> <mappart left="200" top="0" image="engine1/place1/0-2.jpg" /> <mappart left="300" top="0" image="engine1/place1/0-3.jpg" /> <mappart left="400" top="0" image="engine1/place1/0-4.jpg" /> <!--…………--> <mappart left="500" top="0" image="engine1/place1/0-5.jpg" /> <mappart left="600" top="0" image="engine1/place1/0-6.jpg" /> </place>
mappart实际上就是构成该场景的图片及该图片所处的位置:
关于UIPlace
根据上面的描述,UIPlace主要从事以下工作:
- 读取配置文件;
- 从配置文件中读取分块图片的地址;
- 载入分块图片,并指定位置;
- 添加一些UIItem
读取配置文件
该过程UIObject已经完成了,因此只需要重载当配置文件读取完调用的方法OnConfigureFileDownload:
protected override void OnConfigureFileDownload(object sender, DownloadStringCompletedEventArgs e) { if (String.IsNullOrEmpty(e.Result)) return; XElement data = XElement.Parse(e.Result); //………… }
从配置文件中读取分块图片的地址
在mappart中的图片地址,是以相对路径的形式存在的,例如:
engine1/place1/0-4.jpg
该相对路径,具体是指什么呢?由于Silverlight应用程序编译后,会打包成一个Xap的压缩文件,因此,相对路径实际上是相对于该Xap文件的路径,例如,编译后的Xap为:http://localhost/engine/clientbin/test.xap(该地址可以通过方法Application.Current.Host.Source得到),那么engine1/place1/o-4.jpg,其地址为http://localhost/engine/clientbin/engine1/place1/o-4.jpg。
为了便于调用,我写了一个方法,来将该相对地址转换为绝对地址:
public static Uri GetLocalURI(String path) { if (path.ToLower().StartsWith("http://")) { return new Uri(path); } else { return new Uri(Application.Current.Host.Source, path); } }
载入分块图片,并指定位置
在UIPlace中,添加一个Canvas属性,作为画布:
protected Canvas DrawCanvas { get; set; }
把该画布设置成和UIPlace一样大:
protected override void BindingSource() { base.BindingSource(); DrawCanvas.SetBinding(WidthProperty, new Binding() { Path = new PropertyPath("Width"), Mode = BindingMode.OneWay }); DrawCanvas.SetBinding(HeightProperty, new Binding() { Path = new PropertyPath("Height"), Mode = BindingMode.OneWay }); //………… }
修改OnConfigureFileDownload方法,从配置Xml文件中读取每一个地图块的大小、位置,图片。然后将图片绘制在一个同大小的Rectangle上,并按照位置,将该Rectangle添加到画布上:
protected override void OnConfigureFileDownload(object sender, DownloadStringCompletedEventArgs e) { if (String.IsNullOrEmpty(e.Result)) return; XElement data = XElement.Parse(e.Result); //清空画布 DrawCanvas.Children.Clear(); //读取每个地图快的大小 int width=data.Attribute<int>("width",0,XElementExtensions.Int32Parser); int height=data.Attribute<int>("height",0,XElementExtensions.Int32Parser); //开始读取地图快 foreach(var mp in data.Elements("mappart")) { //新建Rectangle Rectangle rect=new Rectangle() { Width=width, Height=height }; //设置该Rectangle的位置 Canvas.SetLeft(rect, mp.Attribute<int>("left", 0, XElementExtensions.Int32Parser)); Canvas.SetTop(rect, mp.Attribute<int>("top", 0, XElementExtensions.Int32Parser)); //准备将图片绘制在Rectangle上 ImageBrush brush = new ImageBrush(); brush.AlignmentX = 0; brush.AlignmentY = 0; //读取图片相对地址 String image=mp.Attribute("image",String.Empty); //贴图 if(!String.IsNullOrEmpty(image)) { brush.ImageSource = new BitmapImage(URIToolkit.GetLocalURI(image)); rect.Fill = brush; } //将Rectangle添加到画布上 DrawCanvas.Children.Add(rect); } }
添加UIItem
当VPlace的VItem集合修改时,需要在UIPlace上反映出来,例如,当VPlace添加一个VItem时,UIPlace也要添加该VItem的UIItem,类似的,当VPlace删除一个VItem时,UIPlace也要删除相应的UIItem,我们透过覆盖BindingSource方法来实现这个需求:
protected override void BindingSource() { base.BindingSource(); DrawCanvas.SetBinding(WidthProperty, new Binding() { Path = new PropertyPath("Width"), Mode = BindingMode.OneWay }); DrawCanvas.SetBinding(HeightProperty, new Binding() { Path = new PropertyPath("Height"), Mode = BindingMode.OneWay }); VPlace.Items.CollectionChanged += new NotifyCollectionChangedEventHandler(OnItemsCollectionChanged); }
OnItemsCollectionChanged方法定义如下:
protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add) { foreach (var vi in e.NewItems) { Children.Add((vi as VItem).UI); } } else if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (var vi in e.OldItems) { Children.Remove((vi as VItem).UI); } } }
继续安室奈美惠的图片: