具体实施方式
本发明延及用于自动生成并跟踪撤消信息以使用户界面对象的开发者无需负责生成并跟踪撤消信息的方法、系统和相应的计算机程序产品。本发明的实施例可包括一个或多个专用计算机和/或一个或多个通用计算机,包括各种计算机硬件,如下文所详细讨论的。
图1说明了本发明的示例性实施例。不同于以往需要开发者对开发者的对象生成并跟踪撤消信息的撤消实现,撤消引擎140构造出用于出现在可视化用户界面设计器的设计表面110中的状态变化的撤消单元。因此,使用设计表面110的开发者无需负责生成并跟踪撤消信息。
撤消引擎140使用运行在设计表面110内的对象的以下特征。首先,关注的对象可以由设计器串行化器保持为某一格式。换言之,由于设计表面110的设计器加载器将对象读写到盘,设计器加载器能够将对象保持为某一格式。其次,当属性改变时,设计表面110内的对象引发属性改变通知。(如,见下文的设计器主机的讨论)。第三,可以使用处理来构造跨越多个较小行动的撤消单元及其相关的属性改变通知。使用这些特征,撤消引擎140能够自动构造它所需要的任意撤消单元。
撤消引擎140听从改变通知124来创建撤消单元,对于图1所示的示例实现,由改变通知服务生成改变通知,在对设计表面110内的对象作出改变时通知撤消引擎140。撤消单元本身,如撤消单元150,包括由串行化引擎130生成的串行化的对象状态。串行化引擎130是用于在向组件作出改变之前和之后保存属性和对象值的整体串行化服务的一部分。设计器主机用来跟踪处理。(例如,由于设计器表面110内的对象在被开发时不是必须的活动对象,设计器主机包围对象来接收并处理针对对象的用户输入。)由此,不需要代码来与撤消引擎交互,并且因此,创建对控制的文本属性的可撤消改变的开发者代码可以用表IV所示的伪代码说明,与表I所示的伪代码相同。
表IV
| textProp.SetValue(control,“Hello”); |
撤消引擎140处理所有别的事物。
也可以执行更复杂的交互。例如,考虑一种将控制居中的算法。在计算X和Y定位来放置该控制之后,布局算法能够在单个撤消行动中组合两个属性设置,如表V所示的伪代码所说明的。
表IV
| using(DesignerTransaction t=host.CreateTransaction(“Center Control”)){PropertyDescriptorCollection props;props=TypeDescriptor.GetProperties(control);props[“X”].SetValue(control,newX);props[“Y”].SetValue(control,newY);t.Commit();} |
整个居中过程进行如下。当在设计器表面110内选择用于居中控制的选项之后,开始处理通知122启动处理,并接收指示对象的状态将要改变的正在改变通知124。作为对处理122的相应,撤消引擎140创建撤消单元150,并且作为对正在改变通知124的响应,使用串行化引擎130来串行化(132)对象的未改变状态。撤消引擎140在撤消单元150内储存串行化的对象状态(例如,撤消单元152和154中的oldX和oldY)。一旦算法完成,已改变通知124指示对象的状态已改变,并且结束处理通知122指示处理的结束。作为对已改变通知124的响应,撤消引擎140使用串行化引擎130来串行化(132)对象的已改变状态,并在撤消单元150内储存已串行化的对象状态(例如,撤消单元152和154中的X和Y)。作为对结束处理通知122的响应,撤消引擎在撤消栈160上储存撤消单元150。
撤消引擎140将撤消单元150当作撤消和重复状态之间的来回切换。选择撤消将对象恢复到选择居中算法之前的位置,并从撤消栈160中删除撤消单元。由于撤消单元150包含了重复居中操作的足够信息,可以将撤消单元放置在重复栈中。选择“重复”会再次执行居中操作,并从重复栈中删除撤消单元(现在为重复单元,因为它位于重复栈中)。
串行化引擎130能够执行两种类型的串行化。对于添加和移除操作,串行化引擎130创建所期望的新组件。然而,对于属性改变,串行化引擎130仅串行化表示该改变所必需的改变。通过尽可能地避免创建新组件,消除了完整组件创建的额外开销,来提高撤消引擎的性能。
以下表V和VI中示出了用于撤消引擎140的示例实现的各种类和方法的伪代码描述。表V所示的UndoEngine类是抽象类,因此能够与撤消存储实现,如撤消栈、串行引擎实现以及用户接口,如可视化用户界面设计器配对。
表V
| namespace System.ComponentModel.Design{public abstract class UndoEngine{public UndoEngine(IServiceProvider p);protected abstract void AddUndoUnit(UndoUnit unit);protected virtual UndoUnit CreateUndoUnit(string name,bool primary);protected virtual void DiscardUndoUnit(UndoUnit unit);public void Dispose();protected virtual void Dispose(bool disposing);protected object GetRequiredService(Type serviceType);protected object GetService(Type serviceType);protected class UndoUnit{}}} |
UndoEngine类的示例方法和属性在表VI所示的伪代码中说明。
表VI
| UndoEngine构造函数public UndoEngine(IServiceProvider provider);行为 创建新UndoEndine参数 System.IServiceProvider provider父服务提供者。UndoEngine需要来自这个提供者的若干服务有用。AddUndoUnit方法 |
| protected abstract void AddUndoUnit(UndoUnit unit)行为 被调用来向撤消栈添加给定的单元参数 System.ComponentModel.Design.UndoEngine.UndoUnit unit要添加的撤消单元。该单元之前通过调用CreateUndoUnit创建。CreateUndoUnit方法protected virtual UndoUnit CreateUndoUnit(string name,bool primary);行为 创建新撤消单元。缺省实现仅返回UndoUnit的一个新实例。参数 System.String name要创建的单元的名称。这是展现给用户的名称。System.Boolean primary当创建一系列嵌套单元的第一个时,primary为真。对任意嵌套单元它为假。UndoEngine缺省对这一参数不作出任何处理,但是可能需要一些撤消(如那些涉及COMIO1eParentUndoUnit设计模式的撤消)的实现来确定主单元及其子单元之间的差异。DiscardUndoUnit方法protected vietual void DiscardUndoUnit(UndoUnit unit);行为 当要解除撤消单元时被调用。在调用这一方法之前关闭撤消单元然后撤消。参数 System.ComponentModel.Design.UndoEngine.UndoUnit unit要解除的单元。Dispose方法public void Dispose();protected virtual void Dispose(bool disposing);行为 布置该对象 |
| 参数 System.Boolean disposing如果布置该对象则为真,如果最终化该对象则为假。GetRequiredService方法protected object GetRequiedService(Type serviceType);行为 返回所请求的服务。如果该服务不可用,该方法抛出NotSupportedException来描述缺少服务。参数 System.Type serviceType要检取的服务的类型。GetService方法protected object GetService(Type serviceType);行为 返回所请求的服务。如果该服务不可用,该方法返回空。参数 System.Type serviceType要检取的服务的类型。 |
表VII中所说明的UndoUnit类是封装可撤消行动的UndoEngine内的嵌套类。缺省的UndoUnit实现听从改变通知,并对每一改变建立事件列表。它使用上述串行化引擎130和串行化服务保存了这些事件。UndoUnit通过若干个公用虚拟方法从UndoEngine直接接收对改变的事件通知。通过展现这些方法,开发者能够从UndoUnit衍生并执行他们所需要的任一另外的逻辑。如期望,开发者能够完全绕过UndoUnit的实现。
表VII
| namespace System.ComponentModel.Design{public abstract class UndoEngine{protected class UndoUnit{public UndoUnit(UndoEngine engine,string name);public string Name{get;}public virtual bool IsEmpty{get;}protected UndoEngine UndoEngine{get;}public virtual void Close();public virtual void ComponentAdded(ComponentEventArgse);public virtual void ComponentAdding(ComponentEventArgse);public virtual void ComponentChanged(ComponentChangedEventArgs e);public virtual void ComponentChanging( |
| ComponentChangingEventArgs e);public virtual voidComponentRemoved(ComponentEventArgs e);public virtual voidComponentRemoving(ComponentEventArgs e);public virtual void ComponentRename(ComponentRenameEventArgs e);protected object GetService(Type serviceType);public void Undo();protected virutal void UndoCore();}}} |
用于UndoUnit的示例方法和属性在表VIII所示的伪代码中说明。
表VIII
| UndoUnit构造函数public UndoUnit(UndoEngine engine,string name);行为 创建新UndoUnit对象参数 System.ComponentModel.Design.UndoEngine engine拥有这一撤消单元的撤消引擎。 |
| System.String name该单元的名称。Name属性public string Name{get;}行为 该撤消单元的名称IsEmpty属性public boolean IsEmpty{get;}行为 如果该撤消单元不包含任何事件则返回真。撤消引擎可以解除空单元。UndoEngine属性protected UndoEngine UndoEngine{get;}行为 返回传入构造函数的撤消引擎。Close方法public virtual void Close();行为 由撤消引擎调用来关闭该单元。当关闭单元,不会再有事件进入其各种事件方法。这里UndoUnit其本身可采取行动来确保当前打开的任何事件都被关闭。ComponentAdded方法public virtual void ComponentAdded(ComponentEventArgs e);行为 当相关的事件从IComponentChangeService引发时,该方法由UndoEngine调用。ComponentAdding方法public virtual void ComponentAdding(ComponentEventArgs e); |
| 行为 当关联的事件从IComponentChangeService引发时,该方法由UndoEngine调用。ComponentChanged方法public virtual void ComponentChanged(ComponentChangedEventArgs e);行为 当关联的事件从IComponentChangeService引发时,该方法由UndoEngine调用。ComponentChanging方法public virtual void ComponentChanging(ComponentChangingEventArgs e);行为 当关联的事件从IComponentChangeService引发时,该方法由UndoEngine调用。ComponentRemoved方法public virtual void ComponentRemoved(ComponentEventArgs e);行为 当关联的事件从IComponentChangeService引发时,该方法由UndoEngine调用。ComponentRemoving方法public virtual void ComponentRemoving(ComponentEventArgs e);行为 当关联的事件从IComponentChangeService引发时,该方法由UndoEngine调用。ComponentRename方法public virtual void ComponentRename(ComponentRenameEventArgs e);行为 当关联的事件从IComponentChangeService引发时,该方法由UndoEngine调用。GetService方法protected object GetService(Type serviceType); |
| 行为 返回给定服务的实例,或者如果无法分解服务则返回空。Undo方法public void Undo();行为 该方法是用于执行撤消行动的公用API。撤消行动在撤消和重复状态之间来回切换其自身,因此调用Undo两次将对象设置回其原始状态。UndoCore方法protected virtual void UndoCore();行为 该方法负责执行撤消行动。它由公用的Undo方法调用。Undo方法其本身工作以即使在UndoCore抛出异常的事件中都保留UndoUnit对象内的一致状态。 |
图2A和2B分别说明了用于在设计表面210的示例用户界面对象212上重新调整大小的操作的示例处理及其相应的撤消单元250。在用户界面对象212的左上角的按下鼠标事件上,从设计表面210向撤消引擎发送开始处理通知,随后为正在改变通知。开始处理通知告知撤消引擎应当在单个撤消单元内捕获一系列改变。改变通知告示撤消引擎保持对象212的当前属性,因为它将要改变。
最初,用户界面212位于位置214,反映为撤消单元250的改变前部分内的(30,30)的定位272和(40,40)的尺寸274。在设计表面210内,用户界面对象212的左上角通过位置216移动到位置218。在抬起鼠标的事件上,向撤消引擎发送结束处理通知,随后为已改变通知。已改变通知告知撤消引擎保持对象212的已改变属性,结束处理通知关闭处理。注意,移动对象212的左上角同时改变了对象的位置和尺寸。因此,在撤消单元250的改变后部分280内储存(10,10)的定位282和(60,60)的尺寸284。通过组合移动-从定位(30,30)到定位(10,10)-和尺寸-从尺寸(40,40)到尺寸(60,60),撤消和重复能够一次同时执行两个动作,这很可能是开发者所期望的,因为从开发者的观点来看,仅执行了一个操作。
撤消单元250也包括额外信息,包括对象名称251、对象类型252以及其它信息253。这一额外信息能够帮助标识与改变的单元相应的对象。例如,当在其上执行操作的对象从设计表面删除然后通过撤消来重新添加时,额外信息将撤消或重复行动指向适当的对象。考虑添加对象的情况,其文本属性被修改-生成两个离散的撤消行动-并且两个行动都是可撤消的。为在由第一个撤消重新创建对象之后重复文本属性改变,撤消单元应当即使在原始对象已经通过删除操作摧毁的情况下也能够定位适当的对象。
图3A和3B分别说明了用于设计表面310上的第三方对话框用户界面对象312的示例处理及其相应的撤消单元350。对话框用户界面对象312包括年龄域313(Age)、性别域315(Sex)、OK按钮317和CANCEL按钮319。在选择性别域315时,从设计表面310向撤消引擎发送开始处理通知,随后为正在改变通知。如图2A-2B一样,开始处理通知告知撤消引擎应当在单个撤消单元内捕获一系列改变。改变通知告知撤消引擎保持对象312的当前属性,因为它将要改变。
最初,性别域315包含“M”,反映为撤消单元350中的改变前部分的包含“M”的性别域372。当焦点离开对话框用户界面对象312的性别域315时,发送已改变通知来告知撤消引擎保持对象312的已改变性别属性,这一已改变的性别属性反映为撤消单元350的改变后部分380的包含“F”的性别域382。在选择年龄域313时,从设计表面310向撤消引擎发送正在改变通知,来告知撤消引擎保持年龄属性,因为它将要改变。最初,年龄域374包含“30”,反映为撤消单元350的改变前部分370内的包含“30”的年龄域374。当焦点离开年龄域313时,发送已改变通知来告知撤消引擎保持对象312的已改变的年龄属性,这一已改变的年龄属性反映为撤消单元350的改变后部分内的包含“-5”的年龄域384。
由于域的有效化一般需要该域首先接收新值,因此“-5”储存在撤消单元350内。然而,域有效化可能需要在可能选择OK按钮和/或年龄域可能失去焦点之前来纠正年龄域。因此,可能向撤消引擎发送另一正在改变通知,在撤消单元350的改变前部分370内在年龄域376中记录“30”。(有效化代码可能将值改回“30”,作为有效化进程的一部分。)将年龄域313改变到合法的值“5”生成已改变通知来告知撤消引擎保持该新值,反映为撤消单元350的改变后部分的包含值“5”的年龄域386。注意,与图2B相似,撤消单元350也包括额外信息,包括对象名称351、对象类型352和其它信息353。
由于对话框用户界面对象312既包括OK按钮317也包括CANCEL按钮319,可能无法完成打开处理。如果选择了CANCEL按钮319,则仅重新运行处理,并且不储存撤消单元350,因为在重新运行之后没有东西可以撤消。如果选择了OK按钮317,则向撤消引擎发送结束处理来关闭该处理,并且储存撤消单元350,允许撤消/重复对话框用户界面对象312内的改变。当关闭对话框时,所有改变一般都作为单个处理来处理,因为大多数用户认为对对话框的改变是单个操作。当然,当打开对话框时,可以将个别改变储存为单独的撤消单元,使它们能够每次撤消或重复一个改变。根据用户界面对象的复杂性,处理可能适当地被嵌套。注意,这一精确的撤消功能对第三方对话框用户界面对象312可用,对象的开发者不需要实现该撤消和重复功能。
本发明也可以按照包括功能性步骤和/或非功能性行动的方法来描述。以下是对可能在实践本发明中执行的行动和步骤的描述。通常而言,功能性步骤按照所达到的结果来描述本发明,而非功能性行动描述用于达到特定结果的更详细的行动。尽管可能以特定的顺序描述功能性步骤和非功能性行动或对其要求权利,本发明不必局限于行动和/或步骤的任一特定顺序或组合。
图4A-4C示出了依照本发明用于生成并跟踪撤消信息的方法的示例行动和步骤。用于处理(414)对可视化用户界面设计器内的用户界面对象的一个或多个改变的一个或多个改变通知的步骤可包括接收(412)作为对用户界面对象的一个或多个改变的响应而生成的一个或多个改变通知。用于保持(418)表示一个或多个改变之前的用户界面对象的初始状态的初始数据以及表示一个或多个改变之后的用户界面对象的随后状态的随后数据的步骤可包括调用(416)串行化引擎来将用户界面对象的至少一部分串行化为适合表示一个或多个改变的串行化格式的行动。
用于从初始数据和随后数据准备(424)用于撤消对用户界面对象的一个或多个改变的撤消单元的步骤可包括从对用户界面对象的一个或多个改变的串行化格式创建(422)撤消单元的行动。对于在对话框上执行的行动,该方法可包括接收(436)选择CANCEL按钮(判决块432的CANCEL分支434)的通知的行动以及取消(438)处理的行动。该方法也包括接收(444)选择OK按钮(判决块432的OK分支442)的通知的行动。
用于储存(454)撤消单元(对对话框或除对话框之外的用户界面对象选择OK按钮)的步骤可包括向撤消栈添加(452)撤消单元的行动。用于听从(458)通知来撤消撤消单元内的一个或多个改变的步骤可包括接收(456)通知来撤消一个或多个改变的行动。用于删除(464)撤消单元的步骤可包括从撤消栈中移除(462)撤消单元的行动。用于使用(468)撤消单元和一个或多个撤消例程来撤消向用户界面对象作出的一个或多个改变的步骤可包括调用(466)一个或多个例程来撤消一个或多个改变的行动。
用于将撤消单元储存(474)为重复单元的步骤可包括将撤消单元作为重复单元添加(472)到重复栈的行动。用于听从(478)通知来重复重复单元中的一个或多个改变的步骤可包括接收(476)通知来重复重复单元中一个或多个改变的行动。删除(484)重复单元的行动可包括从重复栈中移除(482)重复单元的行动。用于使用(488)重复单元和一个或多个重复例程来重复对用户界面对象作出的一个或多个例程的步骤可包括调用(486)一个或多个例程来重复对用户界面对象作出的一个或多个改变的行动。
处于本发明的范围之内的实施例也包括用于携带或在其上储存计算机可执行指令或数据结构的计算机可读媒质。这类计算机可读媒质可以是可由通用或专用计算机访问的任何可用媒质。作为实例而非局限,这类计算机可读媒质包括RAM、ROM、EEPROM、CD-ROM或其它光盘存储、磁盘存储或其它磁存储设备、或可以用来以计算机可执行执行或数据结构携带或储存期望的程序代码方法并可由通用或专用计算机访问的任何其它媒质。当通过网络或另一通信连接(任一硬布线、无线或硬布线和无线的组合)向计算机传输或提供信息时,计算机适当地将该连接视为计算机可读媒质。因此,任一这类连接适当地称为计算机可读媒质。上述的组合也应当包括在计算机可读媒质的范围内。计算机可执行指令包括,如,引发通用计算机、专用计算机或专用处理设备执行特定的函数或函数组的指令和数据。
图5及以下讨论提供了适合在其中实现本发明的计算环境的简要一般描述。尽管并非所需要,本发明在计算机可执行指令的一般语境下描述,计算机可执行指令如程序模块,由网络环境中的计算机执行。一般而言,程序模块包括例程、程序、对象、组件、数据结构等等,执行特定的任务或实现特定的抽象数据类型。计算机可执行指令、相关数据结构以及程序模块代表了用于执行这里披露的方法的步骤的程序代码方法。这类可执行指令或相关数据结构的特定顺序代表用于实现这类步骤中描述的功能的相应动作的示例。
本领域的技术人员可以理解,本发明可以在具有多种类型的计算机系统配置的网络计算环境中实践,计算机系统配置包括个人计算机、手持式设备、多处理器系统、微处理器系统或可编程消费者电子产品、网络PC、小型机、大型机等等。本发明也可以在分布式计算环境中实践,其中,任务由通过通信网络连接(或者通过硬布线链路、或者通过无线链路、或者通过硬布线和无线链路的组合)的本地和远程处理设备执行。在分布式计算环境中,程序模块可以位于本地和远程存储器存储设备中。
参考图5,用于实现本发明的示例系统包括以常规计算机形式520的通用计算装置,包括处理单元521、系统存储器522以及将各类系统组件包括系统存储器522耦合至处理单元521的系统总线523。系统总线523可以是若干种总线结构类型的任一种,包括存储器总线或存储器控制器、外围总线以及使用各类总线结构的本地总线。系统存储器包括只读存储器(ROM)524和随机存取存储器(RAM)525。基本输入/输出系统(BIOS)526,包含如在启动时协助在计算机520内的元件之间传输信息的基本例程,可储存在ROM 524中。
计算机520也可包括用于对磁硬盘539进行读写的磁硬盘驱动器527、用于对可移动磁盘529进行读写的磁盘驱动器528以及用于对可移动光盘531如CD-ROM或其它光媒质进行读写的光盘驱动器530。磁硬盘驱动器527、磁盘驱动器528以及光盘驱动器530分别通过硬盘驱动器接口532、磁盘驱动器接口533和光盘驱动器接口534连接至系统总线523。驱动器及其相关的计算机可读媒质为计算机520提供了计算机可执行指令、数据结构、程序模块和其它数据的非易失存储。尽管这里描述的示例环境采用了磁硬盘539、可移动磁盘529以及可移动光盘531,然而也可以使用用于储存数据的其它类型的计算机可读媒质,包括盒式磁带、闪存卡、数字多功能盘、Bernoulli盒式磁盘、RAM、ROM等等。
包括一个或多个程序模块的程序代码方法可储存在硬盘539、磁盘529、光盘531、ROM 524或RAM 525中,包括操作系统535、一个或多个应用程序536、其它程序模块537以及程序数据538。用户可以通过键盘540、指向设备542或其它输入设备(未示出),如麦克风、操纵杆、游戏垫、圆盘式卫星天线、扫描仪等等向计算机520输入命令和信息。这些和其它输入设备通常通过耦合至系统总线523的串行端口接口546连接到处理单元521。可选地,输入设备也可以通过其它接口连接,如并行端口、游戏端口或通用串行总线(USB)。监视器547或另一显示设备也通过接口,如视频适配器548连接到系统总线523。除监视器之外,个人计算机通常包括其它外围输出设备(未示出),如扬声器和打印机。
计算机520可以在使用到一个或多个远程计算机,如远程计算机549a和549b的逻辑连接的网络化环境中操作。远程计算机549a和549b的每一个可以是另一个人计算机、服务器、路由器、网络PC、对等设备或其它公用网络节点,并通常包括许多或所有上述与计算机520相关的元件,尽管在图4中仅说明了存储器存储设备550a和550b及其关联的应用程序536a和536b。图4描述的逻辑连接包括局域网(LAN)551和广域网(WAN)552,这里示出作为示例而非局限。这类网络环境常见于办公室范围或企业范围计算机网络、内联网以及因特网。
当在局域网网络环境中使用时,计算机520通过网络接口或适配器553连接至局域网551。当在广域网网络环境中使用时,计算机520可包括调制解调器554、无线链路或其它装置,用于通过广域网552,如因特网建立通信。调制解调器554可以是内置或外置的,通过串行端口接口546连接至系统总线523。在网络化环境中,描述的与计算机520相关的程序模块或其部分可储存在远程存储器存储设备中。可以理解,示出的网络连接是示例性的,也可以使用通过广域网552建立通信的其它装置。
本发明可以在不脱离其精神或本质特征的情况下以其它特定形式来实施。描述的实施例在各个方面仅为说明性的而非限制。因此,本发明的范围由所附权利要求书而非之前的描述来指明。所有处于权利要求的等效权利要求的范围和意思之内的变化都包含在其范围之内。