|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Contents
IntroductionIn my last article, I demonstrated using MyXaml to create a simple blog reader. In this article, I'd like to demonstrate how to create vector graphics applications using VG.net's runtime engine in conjunction with declarative markup. In particular, I will be demonstrating the code that creates this working clock:
The Initial Markup<?xml version="1.0" encoding="utf-8" standalone="no"?> <MyXaml xmlns:def="Definition" xmlns="Prodige.Drawing, Prodige.Drawing" xmlns:pds="Prodige.Drawing.Styles, Prodige.Drawing"> <Picture Name="Clock"> </Picture> </MyXaml> The initial markup declares the assembly namespaces and associates them with xmlns prefixes. The default namespace in this case is the Prodige.Drawing vector graphics runtime engine. The Creating The Container FormNormally, one or more Parser parser=new Parser(); object picture=parser.LoadForm(filename, "*", null, null); Type type=picture.GetType(); MethodInfo mi=type.GetMethod("DisplayInForm"); Form form=mi.Invoke(picture, new object[] {new Size(10, 10)}) as Form; form.ShowDialog(); Since the loader doesn't know the name of the Picture, it uses the "*" wildcard to tell the parser to instantiate the first class that it encounters after the <MyXaml> tag. The Clock FeaturesThe following sections discuss how the pieces of the clock are constructed. The Frame
<?xml version="1.0" encoding="utf-8" standalone="no"?> MyXaml xmlns:def="Definition" xmlns="Prodige.Drawing, Prodige.Drawing" xmlns:pds="Prodige.Drawing.Styles, Prodige.Drawing"> <Picture Name="Clock"> <Elements> <Ellipse Name="outerRim" Location="100, 100" Size="200, 200" The clock frame consists of two circles ( The Face
<Ellipse Name="face" Location="114, 114" Size="173, 173" <pds:Style Name="Face"> <pds:Fill> <pds:LinearGradientFill GradientType="TwoColor" Angle="45" A third ellipse and an additional style is created to add the clock face. The Shadow
<Ellipse Name="shadow" Location="102, 102" Size="200, 200" <pds:Style Name="ClockShadow"> <pds:Fill> <pds:SolidFill Color="63, 0, 0, 0" Opacity="0.247058824" /> </pds:Fill> </pds:Style> A shadow is another ellipse and style. Objects are drawn one on top of the other, so, the actual order of the vector graphics elements so far is: <Elements> <Ellipse Name="shadow" Location="102, 102" Size="200, 200" The Numerals
<Picture> <TextAppearance> <pds:TextAppearance Color="192, 192, 255" Size="18" The <Rectangle Name="one" Text="1" Location="220, 124.3782" Size="30, 30" <pds:Style Name="Numeral"> <pds:Fill> <pds:SolidFill Opacity="0" /> </pds:Fill> </pds:Style> Each numeral is placed on top of the clock face, and therefore appears after the "face" First I created "twelve" and positioned it at the top. I selected "twelve" and set the <TransformationReference> <TransformationReference Location="200, 200" Type="Absolute"/> </TransformationReference> Now any change to Rotation will go about that Location. I did a copy/paste of "twelve", creating an identical object in the same place. Let's make that one "three". Change the Rotation property to 90, and the Text property to "3". You now have text rotated about the center of the clock. Now we need to remove the Rotation, but relative to the text center, and not the clock center. Select "three". Right click on the TransformationReference property, and click "Reset". The reference point goes back to Center, but the object does not change position. Now right click on Rotation, and click "Reset". The rotation is gone, but the text does not move. I copied the "twelve" object 11 times, each time setting the Rotation property by a multiple of 30 degrees, changing the Name and Text properties, and doing a Reset on the TransformationReference and Rotation, in that order. The Minute Hand
<Group Name="minute" StyleReference="Minute"> <TransformationReference> <TransformationReference Location="200, 200" Type="Absolute" /> </TransformationReference> <Elements> <Path Name="leftMinute" DrawAction="Fill"> <PathPoints> <PathPoint Point="200.101, 120" Type="Start" /> <PathPoint Point="199.7635, 131.6271" Type="Control1" /> <PathPoint Point="195, 194.9518" Type="Control2" /> <PathPoint Point="195.0503, 198.8948" Type="EndBezier" /> <PathPoint Point="195.1006, 202.8378" Type="Control1" /> <PathPoint Point="197.7291, 205.1745" Type="Control2" /> <PathPoint Point="200, 204.9767" Type="EndBezier" /> <PathPoint Point="200.2, 204.5694" Type="EndLine" /> </PathPoints> </Path> <Path Name="rightMinute" DrawAction="Fill" Scaling="1, -1.213767" <pds:Style Name="Minute"> <pds:Fill> <pds:LinearGradientFill Bounds="0, 0.8, 1.3, 1.3" I asked Frank how he made the hands, and here's what he said: I have to admit, there is a Hack in there. Here is how I did it: I created a 3-point spline for one half of the hand. Then I converted that to a Path, and tweaked the control points a bit. Then I did a copy/paste, creating an identical object in the same place. To mirror, I set the scale X property to -1 (for the minute hand). Then I moved the hand over to the right, using grid snap to align with the other. Since each Path displays a linear gradient, but one displays in the opposite direction (because of negative scaling), together they give it that 3D effect. We want the minute hand to be the bottom-most hand, so it gets declared immediately after the numerals, and has its own style. In this markup, a group (a composite of elements) is being declared, one for each half of the minute hand. The Also note that the entire group uses a The Hour Hand
<Group Name="hour" StyleReference="Hour"> <TransformationReference> <TransformationReference Location="200, 200" Type="Absolute" /> </TransformationReference> <Elements> <Path Name="leftHour" DrawAction="Fill" Rotation="270"> <PathPoints> <PathPoint Point="225.1051, 179.8949" Type="Start" /> <PathPoint Point="217.4753, 179.0615" Type="Control1" /> <PathPoint Point="188.4821, 174.8949" Type="Control2" /> <PathPoint Point="179.3263, 174.8949" Type="EndBezier" /> <PathPoint Point="170.1706, 174.8949" Type="Control1" /> <PathPoint Point="170.1053, 178.2542" Type="Control2" /> <PathPoint Point="170.1706, 179.8949" Type="EndBezier" /> <PathPoint Point="170.4581, 180.1053" Type="EndLine" /> </PathPoints> </Path> <Path Name="rightHour" DrawAction="Fill" Scaling="1.831152, -1" <pds:Style Name="Hour"> <pds:Fill> <pds:LinearGradientFill Bounds="0, 0.8, 1.3, 1.3" The hour hand is almost identical to the minute hand--it consists of a group of elements containing two paths, one for the left side and one for the right side of the hour hand. A separate style is used. The Second Hand
<Polyline Name="second" StyleReference="Second" DrawAction="Edge"> <TransformationReference> <TransformationReference Location="200, 200" Type="Absolute" /> </TransformationReference> <Points> <Vector X="200" Y="200" /> <Vector X="200" Y="135" /> </Points> </Polyline> <pds:Style Name="Second"> <pds:Stroke> <pds:Stroke Color="255, 255, 255" EndCap="Round" StartCap="Round" Being a straight line, the second hand is simpler and is implemented as a Animating The ClockThe only thing left now is to have the clock tell the time! We need to do three things: 1. Add an xmlns to the <MyXaml xmlns:def="Definition" xmlns="Prodige.Drawing, Prodige.Drawing" xmlns:pds="Prodige.Drawing.Styles, Prodige.Drawing" xmlns:wf="System.Windows.Forms"> 2. Instantiate a <Picture Name="Clock"> <wf:Timer Tick='OnTick' Interval='10' Enabled='true'/> ... 3. Implement the event handler: <def:Code language="'C#'"> <reference assembly="System.Windows.Forms.dll"/> <reference assembly="System.Xml.dll"/> <reference assembly="myxaml.dll"/> <reference assembly="Prodige.Drawing.dll"/> <![CDATA[ using System; using System.ComponentModel; using System.Diagnostics; using System.Windows.Forms; using MyXaml; using Prodige.Drawing; class AppHelpers { public Parser parser; public AppHelpers() { parser=Parser.CurrentInstance; } public void OnTick(object sender, EventArgs e) { DateTime n = DateTime.Now; Polyline second=(Polyline)picture.Elements["second"]; Group minute=(Group)picture.Elements["minute"]; Group hour=(Group)picture.Elements["hour"]; second.Rotation = 360F * ((n.Second+ n.Millisecond/1000F)/60F); minute.Rotation = 360F * (n.Minute + n.Second/60F)/60F; hour.Rotation = 360F * (n.Hour + n.Minute/60F)/12F; } } ]]> </def:Code> Wiring Up The Handler In A Compiled AssemblyIf you don't like the in-line code intermingled with the markup, you can wire up the event handler in your own assembly. At the beginning of this article I showed a code snippet for the loader: Parser parser=new Parser(); object picture=parser.LoadForm(filename, "*", null, null); Type type=picture.GetType(); MethodInfo mi=type.GetMethod("DisplayInForm"); Form form=mi.Invoke(picture, new object[] {new Size(10, 10)}) as Form; form.ShowDialog(); By specifying a target object for events (change the first null to a "this" or any other instance of a class that contains the event handler): object picture=parser.LoadForm(filename, "*", this, null); you can copy the ConclusionThe ability for MyXaml to work with third party runtimes provides an exceptionally easy way of plugging in functionality to an application. Compared to the C# code, the markup is less than 1/10th the size, and in my opinion is a lot more readable and easier to edit. And some truly amazing applications can be written in conjunction with VG.net's free runtime vector graphics engine. I hope this article stimulates a lot of discussion and whets your appetite for the beauty of vector graphics! Additional examples of vector graphics are provided in the download. Notes1. The version of MyXaml included in this demo is a pre-release of the next beta (0.95). You can download the source from the CVS site on http://myxaml.tigris.org/ or wait for me to release the next version. 2. The vector graphics engine in this demo is not necessarily the most current. You can download the latest runtime vector graphics engine at http://www.vgdotnet.com/. (Obviously, there is no source for the VG engine. But the runtime is free and fully documented). VG.net is also offering a 30-day time limited beta version of their designer. Further Reading1. MyXaml--XAML-style GUI Generator 2. Vector Graphics and Declarative Animation with Avalon - the Analog Clock 3. Adobe SVG Analog Clock (requires an SVG viewer)
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||