在 Compact Framework 上使用 MyXaml






4.33/5 (10投票s)
一篇关于在 Compact Framework 上应用 MyXaml 的文章。
目录
- 引言
- 缺失的功能
- 运行时编译
CreateDelegate
的实现ISupportInitialize
和Trace
- TypeDecoder
- XPath
- 包含用户定义的事件
- 其他更改
- 测试应用程序
- 结论
引言
我们正在研究自适应用户界面。这些界面应该同时显示在桌面和 PDA 上。当然,在这些设备上的外观会完全不同。研究侧重于决定设计决策的规则。为了能够“即时”创建用户界面,您必须有一个创建控件的机制,并且还需要有关要创建的各种控件的文本描述。对于桌面应用程序,有许多替代方案:UIML、XAML 和 MyXaml(以及其他),后两者不适用于 PDA。在研究了各种可能性之后,我们决定使用 MyXaml(由 Marc Clifton 开发)。在我们完成对 Compact Framework 的移植后,UIML 才可用。
选择 MyXaml 的原因是
- MyXaml 不仅仅是一个解析器,实际上它是一个可以开发应用程序的框架,包括 MVC 模式、工作流等。
- 提供了用于描述界面的 XML 文件的设计器。
- 它使用“普通”窗口控件属性描述。本文描述了我们为在 PDA 上使用 MyXaml 所做的修改。
原始文章中的代码是针对 CF 1.0 的。在此更新中,源代码已“轻松地”移植到 2.0 版本。这意味着,实际代码未更改,只是导入了一个 2.0 项目。
缺失的功能
将 MyXaml 解析器的代码放入 Smart Device 应用程序时,检测到许多缺失的功能
- 运行时编译
CreateDelegate
- TypeDecoder
ISupportInitialize
- XPath
Trace
,并且函数参数的数量存在细微差异。
运行时编译
由于缺少 CodeDom 命名空间,因此无法在运行时编译代码。即使提供了此功能,也可以认为它不太可能在 PDA 上使用,因为其处理器速度不如桌面设备。因此,对 Codebehind
标签的实现进行了调整,使其能够包含已编译的代码。
MyXaml 文件中的示例代码隐藏声明
<def:CodeBehind>
<reference assembly='CodeBehind.dll'/>
</def:CodeBehind>
在解析器中,按以下方式查找引用并导入程序集
foreach(XmlNode codeBehindNode in (
(XmlElement)formNode). GetElementsByTagName("def:CodeBehind"))
{
ArrayList references=GetReferences(codeBehindNode);
foreach (string a_ref in references) {
generatedAssembly = Assembly.LoadFrom(a_ref);
if (generatedAssembly != null)
{
// if code-behind/inline code exists
// and we're successful in generating the code
// then instantiate the all the declared classes.
// Add them to the class collection for this form.
Hashtable classList=InstantiateInlineClasses(generatedAssembly);
foreach(DictionaryEntry entry in classList)
{
classCache.Add(entry.Key, entry.Value);
}
}
}
}
请注意,使用的是 LoadFrom
而不是 LoadWithPartialName
。
除了此解决方案外,还可以通过在窗体初始化期间插入代码隐藏类来使用代码隐藏。在加载窗体之前,实例化代码隐藏的类并将其插入解析器。
public class Startup
{
public void Start()
{
MyOtherClass c=new MyOtherClass();
Parser p=new Parser();
p.AddTarget(c);
Form form=(Form)p.LoadForm("app.myxaml",
"AppMainForm", this, null);
Application.Run(form);
}
}
public class MyOtherClass
{
public void OnDoubleClick(object sender, EventArs e)
{
// ... event handler
}
}
更多详情请在此处 了解。在随附的示例中,应用了最后一种方法。
CreateDelegate 的实现
在 Compact Framework 中,CreateDelegate
函数缺失,但由于存在委托对象,因此可以找到一种解决方法。首先,创建一个 CreateDelegate
类,其中包含事件目标、事件函数参数和方法信息的变量。
public class Createdelegate
{
object eventtarget;
MethodInfo mymethod;
object [] methodparams = new Object[2];
public void Invoke(object sender, System.EventArgs e)
{
methodparams[1] = e;
mymethod.Invoke(eventtarget, methodparams);
}
public Createdelegate(MethodInfo mi,
object eventTarget, object eventObject)
{
eventtarget = eventTarget;
mymethod = mi;
methodparams[0] = eventObject;
methodparams[1] = null;
}
}
在完整框架中调用 CreateDelegate
函数的地方,现在调用一个获取要实现的方法然后创建委托的函数。通过调用此委托,创建了事件处理程序,这就是所需的结果。
private Delegate SearchDelegate(object eventTarget,
string methodeName, object eventObject)
{
Delegate dlg=null;
try
{
MethodInfo implementMethod = eventTarget.GetType().GetMethod(methodeName);
Createdelegate mydlg = new Createdelegate(
implementMethod, eventTarget, eventObject);
dlg = new EventHandler(mydlg.Invoke);;
}
catch(Exception e)
{
Trace.WriteLine(e.Message+" "+e.InnerException);
}
return dlg;
}
ISupportInitialize 和 Trace
ISupportInitialize
也不受支持。但实现它非常简单。该接口有两个成员(BeginInit
和 EndInit
),实现如下:
public interface ISupportInitialize
{
void BeginInit();
void EndInit();
}
Trace
没有实现 WriteLine
。由于该类不能被继承,因此创建了一个新类,它实现了 WriteLine
。请注意,WriteLine
将输出写入控制台,这在正常的 PDA 使用中是不可见的。可以在 PDA 上启动一个控制台,但由于应用程序通常以“最大化”状态运行,因此不容易看到。可以通过在 WriteLine
函数处设置断点来监视消息。
TypeDecoder
TypeDescriptor
中缺失的函数需要更多的工作来实现。实际上,它并未完全实现。在 UIML.net 源代码中,部分实现了此功能。此代码已进行调整,并包含了对其他类型(例如 FormBorderStyle
、Color
)的转换。此模块仍远未完成。
XPath
XPath 是 Compact Framework 中最后一个未实现的项目。幸运的是,XPath 主要用于我们未使用的代码部分。为数不多的使用它的地方可以用 GetElementsByTagName
替换。
其他更改
在解析器中,还有其他几个组件我们没有尝试转换。要么是因为我们不使用它们(例如 Datasource
),要么是因为实现起来太麻烦(例如 StyledListBox
)。
包含用户定义的事件
在最初的文章之后,一位用户希望使用他可以自行定义的事件。为了实现这一点,您必须实现一个名为 CreateHandler
的函数。当事件不是标准 EventHandler
类型时,MyXaml 代码会调用此函数。在您的代码中,您必须包含两部分。
首先,您以正常方式定义所需的事件及其委托
public delegate void WhateverEventHandler(Object sender,
ButtonClickEventArgs e);
public class WhateverEventArgs: EventArgs
{
public WhateverEventArgs()
{
}
}
public class WhateverEventDelegate
{
Object target;
MethodInfo mymethod;
public void Invoke(Object sender, WhateverEventArgs e)
{
mymethod.Invoke(target, new Object[] { sender, e });
}
public WhateverEventDelegate(MethodInfo mi, Object eventTarget)
{
target = eventTarget;
mymethod = mi;
}
}
在此示例中,我没有更改参数,但这很简单。
其次,您必须通过包含 CreateHandler
函数、事件和实际引发事件来链接此事件
public Delegate CreateHandler(Type type, object target, string method)
{
Delegate dlg = null;
MethodInfo implementMethod = target.GetType().GetMethod(method);
if (implementMethod != null)
{
WhateverEventDelegate mydlg =
new WhateverEventDelegate(implementMethod, target);
dlg = new WhateverEventHandler(mydlg.Invoke);
}
return dlg;
}
public event WhateverEventHandler RaiseTheEvent;
protected virtual void OnWhateverEvent(ButtonClickEventArgs e)
{
RaiseTheEvent(this, e);
}
测试应用程序
为了测试修改后的代码是否有效,我们修改了 MyXaml 源文件中的一个示例 simpleCalc.myxaml 文件。本质上,只更改了控件的尺寸。这表明完整框架的所有功能都可以使用(例如样式属性)。此图像显示了 Visual Studio 中 Pocket PC 2005 模拟器上生成的界面。用户定义的事件已使用,但仅用于演示目的。
结论
我们的目标是能够使用相同的代码和相同的 MyXaml 文件为桌面和 PDA 构建用户界面。这可以通过使用两个解决方案(每个平台一个)并使用编译器开关来实现。代码变得不清晰,并且还需要为使用窗体的类和生成的二进制文件使用特定的映射。使用相同的 MyXaml 文件会迫使桌面开发人员使用有限的 Compact Framework 函数集,尤其是在使用的属性方面。因此,我们没有合并代码源。当然,这意味着对于 MyXaml 的每一次修订,都必须进行转换。解析器是更大框架的一部分,但由于它涉及代码的动态生成,然后进行编译,因此无法在 Compact Framework 上使用。所以该框架只能应用于桌面。考虑到 PDA 的小屏幕和可用的处理能力,我们预计 PDA 上只会使用简单的界面。显然,我们没有发明新东西。我们只是结合了各种新闻组和开源项目中提供的信息。我们能够将 MyXaml 移植到 PDA,而且由于 Marc 出色的工作,花费的时间并不长。