You are currently browsing the category archive for the ‘WPF’ category.

Update: Part 2 now available here!

For a recent project I needed an expression parser / compiler / evaluator that could handle Excel-like formulas, but be able to run cross-platform and achieve [near] native code performance.  The Dynamic Language Runtime – which IronPython and IronRuby are built on – looked a pretty neat fit.

First, a little background.  I already had something based on .NET 3.5’s statically-typed expression trees and a modified version of the Dynamic Expression API.  Most of my changes to “Dynamic.cs” were related to handling differences between value types, and some Excel quirks.  To keep things simple, it took a pretty heavy-handed approach to type conversion (largely run-time checks) and imposed some type-related restrictions on the expressions.

The DLR introduces “Expression Trees v2.0”, which are a superset of those from .NET 3.5 and Silverlight 2.  Some interesting things it adds are:

  • DynamicExpression – Describes an operation to resolve at run time, but uses the DLR’s fast dynamic call site caching.
  • Control flow – In addition to simple expressions, many more language concepts can be represented.

IronPython and IronRuby are already a great fit for many use cases.  For an excellent example, check out Pavan Podila’s ScriptConverter – it allows you to use Python expressions and statements in your XAML markup.  The potential uses are huge.

However, for this project I wanted to support as much Excel syntax / oddities as possible and keep a minimal download footprint.

Getting Started – Parsing an expression

A little research revealed I’d probably need a “compiler compiler” or “parser generator”.  I found an awesome utility called TinyPG (by Herre Kuijpers) on CodeProject.  In Herre’s words:

This particular generator is an LL(1) recursive descent parser generator. This means that instead of generating a state machine out of a grammar like most compiler compilers, instead it will generate source code directly; basically generating a method for each non-terminal in the grammar.

So basically you can throw your grammar at it and get source code out.  Nice :)  Here’s some grammar I came up with:

//Tiny Parser Generator v1.2
//Copyright © Herre Kuijpers 2008-2010
<% @TinyPG Namespace="Cjc.ExpressionEngine.Excel.Compiler" %> 

PLUSMINUS    -> @"(\+|-|&)";
MULTDIV    -> @"\*|/";
BROPEN        -> @"\(";
BRCLOSE    -> @"\)";
SBROPEN    -> @"\{";
SBRCLOSE    -> @"\}";
COMMA        -> @",";
PERIOD        -> @"\.";
COLON        -> @"\:";
SHEETBEGIN    -> @"'(?=(''|[^'])*'!)";
SHEETEND    -> @"'!";
QUOTEBEGIN    -> @"@?\""(?=(\""\""|[^\""])*\"")";
QUOTEEND    -> @"\""";
QUOTED        -> @"(\""\""|[^\""])*";
SNGQUOTEBEGIN    -> @"'(?=[^']*'[^!]?)";
SNGQUOTEEND    -> @"'(?=[^!]?)";
SNGQUOTED    -> @"(''|[^'])*";
TRUE        -> @"[Tt][Rr][Uu][Ee]";
FALSE        -> @"[Ff][Aa][Ll][Ss][Ee]";
NULL        -> @"[Nn][Uu][Ll][Ll](\(\))?";
CELLID        -> @"\$?[a-zA-Z]+\$?[0-9]+";
CONSTRUCTOR    -> @"new\s[a-zA-Z_][a-zA-Z0-9_\.]*";
METHODNAME    -> @"[a-zA-Z_][a-zA-Z0-9_\.]*[a-zA-Z0-9_](?=\()";
IDENTIFIER    -> @"[a-zA-Z_][a-zA-Z0-9_\.]*[a-zA-Z0-9_]";
EVAL        -> @"\{Eval\s[a-zA-Z0-9_\-\.\s]*?\}";
DATA        -> @"\{Data\s[a-zA-Z0-9_\-\.\s]*?\}";
ASSIGNID    -> @"[a-zA-Z_][a-zA-Z0-9_\.]*(?=\s?=\s?)";
INTEGER    -> @"(\+|-)?[0-9]+";
NUMBER        -> @"(\+|-)?[0-9]*\.[0-9]+";
COMPARE    -> @"==|<=|>=|>|<|=|!=|<>";
EQUALS        -> @"=";
SIGN        -> @"\+|-";
INVALID    -> @"#VALUE!";
EOF        -> @"^$";

[Skip] WHITESPACE -> @"\s+";

Start        -> (Assignment|CompareExpr)? EOF;
Assignment    -> ASSIGNID EQUALS CompareExpr;
CompareExpr    -> AddExpr (COMPARE AddExpr)?;
AddExpr    -> MultExpr (PLUSMINUS MultExpr)*;
MultExpr    -> Sign (MULTDIV Sign)*;
Params        -> CompareExpr? (COMMA CompareExpr)*;
Constructor    -> CONSTRUCTOR (BROPEN Params? BRCLOSE | SBROPEN Params? SBRCLOSE);
Method        -> METHODNAME BROPEN Params? BRCLOSE;
String        -> QUOTEBEGIN QUOTED QUOTEEND | SNGQUOTEBEGIN SNGQUOTED SNGQUOTEEND;
Sheet        -> SHEETBEGIN SNGQUOTED SHEETEND;
Range        -> Sheet? ( CELLID (COLON CELLID)? );
Identifier    -> IDENTIFIER;
Eval        -> EVAL;
Data        -> DATA;
Sign        -> SIGN? (Method | Constructor | Range | Identifier | Eval | Data | Atom);
Atom        -> TRUE | FALSE | NULL | INTEGER | NUMBER | String | INVALID | BROPEN CompareExpr BRCLOSE;

Building this in TinyPG gives you three source files: Scanner.cs, Parser.cs and ParseTree.cs.  Most of these have partial classes so it’s easy to extend them (TinyPG generates code from a template; you could easily change that if you want more control).

Building an Expression / Abstract Syntax Tree

To build a tree from an expression, simply create a Scanner, wrap it in a Parser and feed it your expression.  We’ll extend the ParseTree [partial] class with code similar to this:

private Expression GetNodeExpression( ParseNode node )
{
    switch ( node.Token.Type )
    {
        case TokenType.Start:
            return GetStartExpression( node );

        case TokenType.Assignment:
            return GetAssignmentExpression( node );

        case TokenType.CompareExpr:
            return GetComparisonExpression( node );

        case TokenType.AddExpr:
            return GetAddExpression( node );

        case TokenType.MultExpr:
            return GetMultiplyExpression( node );

        case TokenType.Atom:
            return GetAtomExpression( node );

        case TokenType.Range:
            return GetRangeExpression( node );

        case TokenType.TRUE:
            return Expression.Constant( true );

        case TokenType.FALSE:
            return Expression.Constant( false );

        case TokenType.NULL:
            return Expression.Constant( null );

        case TokenType.STRING:
            return GetString( node );

        case TokenType.INTEGER:
            {
                int iVal;
                var value = node.Token.Text;

                return int.TryParse( value, out iVal )
                    ? Expression.Constant( iVal )
                    : Expression.Constant( long.Parse( value ) );
            }

        case TokenType.NUMBER:
            return Expression.Constant( decimal.Parse( node.Token.Text ) );

        case TokenType.Identifier:
            return GetVariableExpression( node );

        case TokenType.Method:
            return GetMethodExpression( node );
    }

    return Expression.Constant( node.Token.Text );
}

The helper methods (GetMultiplyExpression etc) are pretty simple; you can see the full source on CodePlex.

Not Using the DLR

So far we haven’t done anything specific to the DLR.  You could go ahead and build a regular .NET 3.5 [statically-typed] expression tree from this and it’d work great.  In fact, if you only needed to handle numeric values the implementation could be pretty trivial – just Expression.Convert all values to System.Decimal, wrap the tree in Expression.Lambda, call Compile and invoke the delegate.  All too easy – but where’s the fun in that? :)

A Small Teaser

To get an idea how something like this might be used, you can check out a small teaser right here (or click the image below).

image

Continue to Part 2

You can get the source code for YouCube 2.0 here.  It’s lightly commented, mainly to draw attention to the dirty hacks :)

See my earlier post for a brief description of what’s going on and note the following:

  • The main class of interest is CjcWebBrowser.  It starts a worker thread to take snapshots of the browser control and updates the WriteableBitmap through Dispatcher.Invoke.
  • There’s some obsolete code in there (mainly in the NativeMethods folder).  It could use some refactoring :)
  • It’s tempting to revert to using the Windows Forms WebBrowser control (instead of the WPF/3.5 one).  It already has a ‘DownloadProgress’ event and seemed to perform a bit better (but I could be wrong on that).  Let me know if you get chance to try it…

UPDATE 3: YouCube 3.0 (based on Google Chromium) right here.

UPDATE 2: Now interactive!  See here for details.

UPDATE: Added a directional light, specular highlight and trackball / mouse control…  Just because I could :)

As you know, .NET 3.5 SP1 includes a WebBrowser control.  The current implementation is just a wrapper around the MSHTML ActiveX control, which unfortunately prevents you from doing crazy things to it through WPF.

The ActiveX control itself can be queried for the IViewObject interface and have its Draw method called.  The WPF WebBrowser doesn’t expose this directly, but fortunately the Windows Forms WebBrowser does (alternatively you could host MSHTML directly).

All that remains is to get the image into a WriteableBitmap…  Then the fun can begin :) :

image

You can try the result here (ClickOnce application).  The source code is available here.  Note the browser isn’t interactive [yet] :)

kick it

Most of the source code for my physics demos is available on CodePlex (except for the WPF 2D one; I’ll get that moved at some point).  To save you from having to hunt around I’m listing the main ones here.  Enjoy! smile_teeth

Silverlight 1.1 2D (wheels)

One of my first Silverlight efforts, using the BulletX library; as soon as I get some time I’ll fix the irritating performance problem (stuttering frame rate), I promise!

Demo: http://www.chriscavanagh.com/Chris/Silverlight/Physics2D-1/TestPage.html
Source: http://www.codeplex.com/SilverlightPhysics

WPF/XBAP 3D (beachballs)

One of my favorites (I like the specular highlights smile_regular) using the BulletX library.  Don’t forget you can rotate the whole scene by dragging, or zoom in/out by right-dragging.

Demo: http://www.chriscavanagh.com/Chris/CJC.Bullet/BulletDemo1.xbap
Source: http://www.codeplex.com/XBAP3DPhysics

WPF / Flade (controllable car)

The already awesome Flade engine demo, running as an XBAP.

Demo: http://www.cubpack99.com/pack99/cjc.dynamicsengine.xbap
Source: http://www.chriscavanagh.com/chris/CJC.DynamicsEngine.zip

WPF 2D (car, eggs)

Based on the Newton Dynamics engine.  This would be sweet as an XBAP, but would need to use another (.NET based) physics engine such as BulletX

Demo (EXE): http://www.chriscavanagh.com/Chris/Source/WPFPhysics1_exe.zip
Source: http://www.chriscavanagh.com/Chris/Source/WPFPhysics1.zip, http://www.chriscavanagh.com/Chris/Source/NewtonDynamics.zip

Mike Heydlauf (also known as Mike Walters) has created an awesome WPF physics game called Chicken Coop.  It reminds me a bit of ChuChu Rocket, but with physics and no cats.  It includes great tutorials; I challenge you to not get hooked! smile_nerd  Some details from ChiefChicken himself:

Chicken Coop is a physics-based puzzle game developed using the Newton Dynamics physics engine. Chicken Coop was written in C# using .Net 3.0 and WPF (WCF for back-end services and level publishing). Inspired by the classic puzzle game Lemmings, I decided to write Chicken Coop because I wanted a Lemmings-style game that shipped with a level editor, and because I wanted a good excuse to learn WPF.  I know WPF isn’t necessarily targeted for game development, but it really lets you do some powerful things easily (for example: checkout how the level selection buttons are actually “live” levels when you hover over them). 

Be sure to start with the tutorial levels to get a feel for the game before diving into some of the more difficult levels.  I would also encourage players to experiment with the level editor and publish some puzzles of their own design.  I hope you enjoy playing the game as much as I have enjoyed creating it.

Thanks,

Mike Heydlauf (ChiefChicken)

Follow

Get every new post delivered to your Inbox.

Join 42 other followers