虚拟实验室引擎的开发和实现(三、Virtual Object)

在前一篇日志里,我用两个UML图描述了引擎中所使用的数据对象和界面渲染对象。本文将创建VObject类。VObject是引擎中所有虚拟对象的父类,包括一些公共的属性和方法。

image

VObject内部,主要通过以下顺序来创建数据。

image

属性

让我们看看VObject有哪些属性:

属性名 类型 说明
Name String 名称
ID Guid 唯一的编号值
Width Int32
Height Int32
CenterX Int32 中点横坐标(相对于VObject)
CenterY Int32 中点纵坐标(相对于VObject)
TopX Int32 顶点横坐标(相对于所在的Place)
TopY Int32 顶点纵坐标(相对于所在的Place)
X Int32 中心横坐标(相对于所在的Place)
Y Int32 中心纵坐标(相对于所在的Place)
ConfigFile String 配置文件
UI UIObject UI

CenterX、TopX、X及CenterY、TopY、Y的关系如下:

image

INotifyPropertyChanged接口

Silverlight提供了数据绑定功能。数据绑定为基于 Silverlight 的应用程序提供了一种显示数据并与数据进行交互的简便方法。数据的显示方式独立于数据的管理。UI 和数据对象之间的连接或绑定使数据得以在这二者之间流动。绑定建立后,如果数据更改,则绑定到该数据的 UI 元素可以自动反映更改。同样,用户对 UI 元素所做的更改也可以在数据对象中反映出来。

image

为了使源对象的更改能够传播到目标,必须实现 INotifyPropertyChanged 接口。INotifyPropertyChanged 具有 PropertyChanged 事件,该事件通知绑定引擎源已更改,以便绑定引擎可以更新目标值。

VObject实现了INodifyPropertyChanged接口,并利用OnPropertyChanged函数来发送目标更改通知:

public abstract class VObject:INotifyPropertyChanged
{
    //…………

    protected void OnPropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
}

例如使用以下形式的属性设置函数来设定VObject的X坐标:

public int X
{
    get
    {
        return m_X;
    }
    set
    {
        if (m_X != value)
        {
            m_X = value;
            OnPropertyChanged("X");
            OnPropertyChanged("TopX");
        }
    }
}

public int TopX
{
    get
    {
        return m_X - m_CenterX;
    }
    set
    {
        m_X = value + m_CenterX;
    }
}

 

 

从XML中读取属性值

在上一篇日志里,我提到使用一个XML文件传递数据,对于VObject对象,该XML文件的形式可能为:

<object name='周慊' width='147' height='144'
        centerX='73' centerY='144'
        x='1100' y='780'
        file='person.xml'
        id='80d76e6b-08d6-4225-bdcc-bf68ca19ca58' />

我利用函数Parse来转换数据,Parse是一个重载的函数,参数分别为String和XElement:

public virtual void Parse(string data)
{
    XElement xElement = XElement.Parse(data);
    Parse(xElement);
}

protected virtual void Parse(XElement xElement)
{
    if (xElement.Attribute("X") != null)
    {
        if (!String.IsNullOrEmpty(xElement.Attribute("X").Value))
        {
            X = int.Parse(xElement.Attribute("X").Value);
        }
        else
        {
            X = 0;
        }
    }

    //…………
}

扩展XElement类

注意上面的第二个函数,要解析XElement的属性,至少需要两个if判断:判断该XML中存在属性,判断该属性有值。如果属性很多,为每个属性添加那么多if判断是一件很麻烦的事情。我使用一个扩展方法来解决这个它。扩展方法是我喜欢的一个C# 3.0特性,允许为类添加额外的方法,我的代码如下:

public static class XElementExtensions
{
    public static String Attribute(this XElement xelement, String name, String defaultvalue)
    {
        if (xelement.Attribute(name) == null)
        {
            return defaultvalue;
        }
        else if (String.IsNullOrEmpty(xelement.Attribute(name).Value))
        {
            return defaultvalue;
        }
        else
        {
            return xelement.Attribute(name).Value;
        }
    }
    public static T Attribute<T>(this XElement xelement, String name, T defaultvalue,
        Func<String,T> func)
    {
        if (xelement.Attribute(name) == null)
        {
            return defaultvalue;
        }
        else if (String.IsNullOrEmpty(xelement.Attribute(name).Value))
        {
            return defaultvalue;
        }
        else
        {
            return func(xelement.Attribute(name).Value);
        }
    }

    public static Func<String, Guid>    GuidParser      = x =>  new Guid(x);
    public static Func<String, int>     Int32Parser     = x =>  Int32.Parse(x);
}

于是上面的函数可以写成:

protected virtual void Parse(XElement xElement)
{
    Name = xElement.Attribute("name", String.Empty);
    ID = xElement.Attribute<Guid>("id", Guid.NewGuid(), XElementExtensions.GuidParser);
    ConfigFile = xElement.Attribute("file", String.Empty);
    Width = xElement.Attribute<Int32>("width", 0, XElementExtensions.Int32Parser);
    Height = xElement.Attribute<Int32>("height", 0, XElementExtensions.Int32Parser);
    X = xElement.Attribute<Int32>("x", 0, XElementExtensions.Int32Parser);
    Y = xElement.Attribute<Int32>("y", 0, XElementExtensions.Int32Parser);
    CenterX = xElement.Attribute<Int32>("centerX", 0, XElementExtensions.Int32Parser);
    CenterY = xElement.Attribute<Int32>("centerY", 0, XElementExtensions.Int32Parser);
}

创建UI及初始化操作

在VObject里,使用CreateUI函数来创建UI对象,使用Initialize函数来初始话一些对象(例如VPlace类可以用该函数来初始化VItem容器),这两个函数都是抽象的,需要子类实现:

protected abstract void CreateUI();
protected abstract void Initialize();

最后,VObject的构造函数为:

public VObject(String data)
{
    Initialize();
    Parse(data);
    CreateUI();
}

image

posted on 2011-05-06 14:49  carekee  阅读(451)  评论(0)    收藏  举报