Thursday, December 30, 2010

Signal-to-Noise Ratio, ROI and ROA in Software Development

PhilosophicalI normally focus on very technical posts with a lot of code. Recently, I've been asked a few questions and had some interesting conversations that prompted this more philosophical post. I'd like to take a step back and look at the software development process in general and you as the specific developer, to share some of the strategies I've used to help me be happy, productive, and successful with my career. I hope that learning about signal-to-noise ratio, return on investment (ROI) and return on aggravation (ROA) will help you look at how you spend your time as a developer and perhaps find areas to improve.

Signal-to-Noise

One thing someone asked me was, "How do you take on so much? Between a full-time job, your blog, your open source projects, training, and being with your family, where do you find the time?" Obviously, we all have the same hours in the day. One advantage I had was starting my own business and running it for several years. This forced me out of my comfort zone and required that I learn about sales and marketing. It also forced me to examine my time and learn how to maximize my time and organize my day.

I'm a huge fan of the "rule of three" that is a simple method for setting goals. I don't follow it religiously but the gist is simple: set three goals for today, three goals for the week, three goals for the month, and three goals for the year. Obviously the longer goals are bigger ones, and the shorter goals are baby steps to reach it. I'm constantly re-evaluating my goals and adjusting as I go, and even wrote myself a letter with some commitments for 2010 that I’ll open January 1st 2011 to see how well I did and then write another letter for next year.

Signal-to-Nose

One thing I am constantly aware of is my signal-to-noise ratio. In fact, the easiest way to explain this is to look at how I manage Twitter feeds. On the "publisher" side it would be extremely inefficient for me to scour the Internet all day long for information to post. This would disrupt my workflow, and take time away in chunks throughout the day. In essence, it would introduce noise that I don't need. During working hours, the "signal" is the work I do with Wintellect, so remaining focused on that outside of scheduled breaks is paramount. Instead, I allocate time in the morning (I get up between 5 am and 6 am) to review feeds and posts, and then schedule my informational Tweets. I know that dumping a dozen links on you would also be "noise" for you, which is why I have the tweets released on a schedule so you have time to absorb and respond to one before the next goes out.

As a "consumer" I do something similar. I follow a lot of people. Some people are interesting, some are friends, some I follow as a courtesy and others are great sources of information. Again, if I were to wade through the full stream of tweets I receive, I would be drowned in noise. I can't tell you how many people insist on posting nothing but FourSquare updates or tweeting about how they just walked to the mailbox. This is noise. So, to avoid that, I create lists. I have lists for Silverlight developers, for people who use or talk about MEF, and for people in the local area. My most valuable list is the "Signal" list. People with valuable information make it to that list and I scan it frequently. People who mostly make small talk or have their Twitter linked to games usually don't make it or stay on that list.

Signal and noise happen in other areas as well. When your work day is finished, what does the rest of your evening look like? Watch a movie? Noise. Read a book about unit testing? Signal. Watch the game? Noise. Build a reference project to learn a new concept? Signal.

Don't get me wrong. We all need a little noise. I take breaks during the day to read fiction novels and watch shows like "24." It's just that I am always focused on the ratio and try to keep the signal end as high as possible. That's why I sacrifice a lot of sitting on my thumbs or watching TV for doing things like my open source projects, blog posts, and writing. Oh - one more thing. Being forced to watch a show because it comes on at 8pm? Noise. Watching it on demand when you're ready? Signal!

Return on Investment
The next area I commonly focus on is return on investment. I'm constantly evaluating what the ROI of various activities is and prioritizing the ones with the biggest returns. It's like a personal stock market - I want to invest heavily in the stocks that are poised to produce major gains, but I'll still round out my portfolio with some others that may be higher risk and/or lesser yield. Where are some of the areas I can improve my return on investment?

Return on Investment

In development, I can think of several ways. Building a new framework from scratch for each new project might be fun, but it certainly is a big investment. Creating a reusable framework based on common patterns I see in different projects is an enormous return on investment because I can slash days from projects by having the foundation ready, "out-of-the-box." Jounce is an example of a project I was tracking internally and eventually released so others could take advantage of it as well.

Unit testing is another example of ROI. I know many developers who still shy away thinking unit tests add extra ritual/ceremony, provide little value and will only add time to the project. I value unit tests from experience because I know I will receive a major return on the initial investment. In Silverlight projects, unit tests enable me to finish my view models before the design-team has written a single line of XAML for the views. In projects like Sterling, it saves me hours when I make a major change because I can run the unit tests and make sure the major pieces of functionality haven’t broken before checking in a change.

Being organized with my emails is also a huge ROI. People have different approaches to their inbox. Personally, I focus on an empty inbox. The trick, however, is that I don't always respond immediately to emails. When an email comes in, I may respond if I can. Otherwise, it will get filed in a set of folders by topic (this helps me narrow searches when trying to find older correspondence) and flagged. Outlook makes it easy to right click and turn an e-mail into a task, so I can constantly track items I need to return to.

Tools can also provide a return on investment. For me, software like ReSharper makes me far more productive than I would be without. The short cut keys, refactoring tools, and rule checks all help me develop quality code faster. If you are typing out every key word and manually implementing interfaces, your ROI is suffering. Knowing the short cuts and getting familiar with them will only help increase your ROI as you develop code.

A final thought on this is patterns. I've heard people complain that patterns are too prescriptive and often people follow them just to say "Hey, look, I implemented a chain of responsibility." The truth is that patterns are very powerful and useful, and help to increase ROI. I like to refer to this as "pattern vocabulary" or how knowing a solution can change your way of thinking. If you haven't been exposed to a way of doing something, it is harder to figure it out when you need to solve a problem. When you are very familiar with different patterns and solutions, you use this vocabulary to tackle problems and start by applying patterns to see if they fit. This can have a hugely positive impact on your productivity levels - to me, learning patterns is a huge ROI.

Return on Aggravation

The last piece is a phrase I learned from a former boss so I can't take credit for it, but it is a powerful idea. It's "return on aggravation." Most of what you do each day may come with a level of aggravation. The question is what levels are reasonable, and when the ROA is greater than the ROI.

Return on Aggravation

As an example, consider writing a book. It's a goal that I have, and I've considered some offers and projects. However, right now, I haven't taken on a full project because the ROA is too great. The ROI is a little bit of cash (most technical writers know you don't write technical books to get rich) and a lot of recognition. There is an intangible reward for taking something I am passionate about (teaching and mentoring) and creating a vehicle to get it out there. The aggravation is the time commitment and things I would have to sacrifice to make it happen without driving my publisher, family, and eventually me, crazy.

Another example is forum posts. I love visiting the forums and helping where I can. I also am happy to share my experiences when some posts are opinionated and looking for feedback. These types of discussions, however, can quickly revert to back-and-forth arguments and debates. There is very little ROI (most often the urge to post a retort is just to get the last word in and say, "I'm right") and a lot of aggravation, so I typically stay out of those types of discussions on forums and Twitter.

You may find the ROA factors into your job as well. I've been in positions in the past where I had to look at what I wanted to do with my career and how much ROA (commutes, old technologies, 3am debug sessions) existed. One of the reasons Wintellect is such a terrific company for me is because the ROA is very low, compared to the ROI of working with a talented group of peers on amazing projects. It's important to note that I didn't just stumble into Wintellect - I had to make a decision and focus on taking the steps I could so they would consider me for the position.

You might think writing long blog posts like this also raises my ROA, but it doesn't. I actually enjoy blog posts because they are a less formal tone and help me offload thoughts and ideas. There is a bit of aggravation related to the time it takes but the ROI of being able to express myself and then create connections from the people who benefit from reading the posts far outweighs the cost.

One last area that ROA factors in is what some may call "over-engineered frameworks." I've been guilty in the past. I'm a big fan of frameworks that help steps magically fall into place and reduce the burden on the developer. However, I've also found that in many cases, the perceived ROI is actually quite small compared to the ignored ROA of developing it. You’ve probably done it, too. Have you found yourself spending hours or days trying to build that magic construct that uses recursive reflection and T4 templates so you can configure an item with an enumeration instead of a magic string? Do you suspect that sometimes the ROA of the time you invested in that solution, the complexity of maintaining it, and the performance hit it causes the application during runtime might perhaps outweigh the ROI of enforcing a value that might easily have been addressed by a few constants? The constants might not be as elegant, but it's important to keep in mind the entire scope of the system when making impactful decisions within your software.

So there you have it, in a nutshell. While I do fit a lot into my schedule, it is always in the context of understanding how each decision, step, post, or line of code will impact my day. I am constantly trying to raise the signal and lower the noise, increase the ROI of what I do while keeping the ROA low.

How about you - what are your techniques for maximizing your productivity?


Jeremy Likness

Tuesday, December 28, 2010

Using Sterling in Windows Phone 7 Applications

Sterling is an object-oriented database that I created to facilitate ease of serialization of data in both Silverlight 4 (soon 5) and Windows Phone 7 projects. Sterling was designed with a few goals in mind. It is not a true relational database and not intended to replace one. I'm confident certain offerings such as SQLite or even a trimmed down version of SQL Server will make their way to the phone eventually, backed by experienced teams and highly optimized code.

Instead, I set out with a few fundamental goals:

  1. Keep it as lightweight as possible — no one wants to add a huge DLL just to facilitate some functionality
  2. Keep it extensible and flexible to address as many needs as possible
  3. Take advantage of LINQ to Objects to provide keys and indexes for ultra-fast querying of data and lazy-deserialization from isolated storage
  4. Keep it non-intrusive — users should not have to modify their existing types to use the database system, such as inheriting from a base class

I believe I've successfully addressed those needs and Sterling is quickly being adopted in major projects. The community has contributed some excellent suggestions and even modifications to allow for a very rich feature set. Sterling currently supports:

  • Out-of-the-box support for most native value types
  • Automatic enum support
  • Cycle detection and will handle self-referencing classes for both serialization and de-serialization scenarios
  • Tuple and Lazy support on the phone
  • Support for Nullable<T>
  • Handles ICollection, IList, and IDictionary
  • Supports base classes (will serialize/deserialize the derived types)
  • Provides keys and indexes (covered indexes means in-memory querying)
  • Lazy loading of serialized values
  • Multiple database support (for versioning or partitioning of data in complex applications)
  • Custom serialization for types not supported out of the box
  • Custom logging of database events
  • In spite of all of these features, Sterling still holds it own for speed and because it uses a binary format, is quite compact on disk

This post is intended to introduce you to Sterling and also to provide guidance for using it on the Windows Phone 7. I am not supplying a full project as this is being built for the full 1.0 release, but I've received numerous requests for clarification and guidance on the phone, so this post is intended to fill that gap until the full Sterling documentation is released.

Download Sterling

Your first step will be to download Sterling. As of this post, you'll want to grab the latest source as there have been numerous bug fixes and optimizations that were not in the latest formal release. These will be integrated into the 1.0 RTM which is slated for early 2011. You can visit this link and choose "Download" in the box to the upper right that indicates "Latest Version."

Build Sterling

Navigate to the SterlingSln directory and choose the WindowsPhoneSterlingSln.sln file to open the source code for Windows Phone 7. It is up to you whether you want to build the project and include Wintellect.Sterling.WindowsPhone.dll in your project, or simply use a project reference. As of this blog post, the DLL weighs in at a light 72 kilobytes on disk.

Sterling Under the Covers

Before you wire Sterling into your Windows Phone 7 application, it helps to understand how it works under the covers. Below is a diagram of the rough directory structure that Sterling uses to serialize data:

Sterling Folder Structure

Sterling creates an aptly named Sterling folder at the root of isolated storage. It contains a file called db.data which simply maps database names to folders that are named sequentially. This shortens the folder length but also helps to avoid collisions with type names in the folder structure and/or name.

The first database receives a subfolder named 0. The root of that folder contains a tables.dat file that maps type names for classes that Sterling is using to another set of subfolders, one for each type. The first class to be referenced for the database will receive a subfolder 0 as well.

To put things in perspective, if you have a database named "Contacts" and a type called "ContactEntity" then the path to the data for the ContactEntity is Sterling\0\0 where 0 represents the first database and 0 represents the first type. A second database will reside at Sterling\1 and a second type in the first database will reside at Sterling\0\1.

Within the type folder, there are several files. There is always a keys.dat file that maps key values to ordinals. For example, if you have a key that is a GUID, this file will map the GUID value to "0" for the first record, "1" for the second record, etc. Each row in the table is stored as a separate file for fast access. If you defined any indexes for the table, the indexes are mapped to keys in a file named {indexname}.dat.

Sterling serializes the rows as soon as they are saved. For efficiency, Sterling does not flush the keys each time. Imagine saving 1,000 records. If Sterling serialized the keys each time, it would have to serialize a 1 row record, then overwrite that with a 2 row record, etc. until the 1,000 rows were written. Instead, Sterling will wait until you call the Flush method before writing the keys and indexes to disk.

As a best practice, you should flush after serializing types. If you are saving or updating a single type, flush after the save. If you are performing a bulk operation, wait until after all of the types are serialized and then call flush. Doing so ensures the integrity of the database. Sterling tracks a "dirty flag" on the keys and indexes, and when the application tombstones, it will flush any keys or indexes that haven't already been saved. You will speed the tombstone process if the keys/indexes have already been flushed. No flush is required after queries or other read-only operations.

Creating a Sterling Database

Once you are ready to begin using Sterling, you will need to define a database. Typically each application will have a single database that supports multiple types. Sterling provides the facility to use multiple databases for ease of versioning and for data partitioning. A database is simply a collection of types that will be persisted.

A Sterling database inherits from the BaseDatabaseInstance. There are two required overrides. The first defines the name of the database and should be a unique name within your application. The second defines what types the database will support. To define a type, simply use the base CreateTableDefinition. You must supply the type that will be serialized, along with the type of the unique key. Keys can be any value supported by Sterling. Keys that are not directly supported, or are based on complex classes, must have a serializer defined (this will be discussed later). For now, assume a simple integer key. The base method takes one parameter, and that is a lambda expression that instructs Sterling how to get the key value for a given type.

Here is an item view model, taken and modified from the default Windows Phone 7 project:

public class ItemViewModel : INotifyPropertyChanged
{
    private int _id;

    public int Id
    {
        get { return _id; }
        set
        {
            if (value != _id)
            {
                _id = value;
                NotifyPropertyChanged("Id");
            }
        }
    }

    private string _lineOne;
    public string LineOne
    {
        get
        {
            return _lineOne;
        }
        set
        {
            if (value != _lineOne)
            {
                _lineOne = value;
                NotifyPropertyChanged("LineOne");
            }
        }
    }

    private string _lineTwo;
    public string LineTwo
    {
        get
        {
            return _lineTwo;
        }
        set
        {
            if (value != _lineTwo)
            {
                _lineTwo = value;
                NotifyPropertyChanged("LineTwo");
            }
        }
    }

    private string _lineThree;
    public string LineThree
    {
        get
        {
            return _lineThree;
        }
        set
        {
            if (value != _lineThree)
            {
                _lineThree = value;
                NotifyPropertyChanged("LineThree");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (null != handler)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

To notify Sterling about this type is as simple as the following code, which defines a database to store the item:

public class ItemDatabase : BaseDatabaseInstance 
{

    public override string Name
    {
        get { return "ItemDatabase"; }
    }

    protected override System.Collections.Generic.List<ITableDefinition> _RegisterTables()
    {
        return new System.Collections.Generic.List<ITableDefinition>
        {
            CreateTableDefinition<ItemViewModel,int>(i=>i.Id)
        };
    }
}

Supporting other types is as simple as adding a comma after the CreateTableDefinition call and using the same method to register another type.

Adding Indexes

Sterling allows the definition of indexes as well. Indexes are stored in memory, so you must take care when deciding what indexes will be used - the trade-off is memory vs. the speed of serialization/de-serialization. Indexes are intended to provide fast in-memory lookup of data. Once the data is filtered and selected, the full type can be de-serialized from disk. This lazy loading ensures that queries and look ups are fast and efficient.

To add an index to a type registration, use the WithIndex extension method. Indexes always include the key. The format for an index is {classType,indexType,keyType}. Sterling supports indexes with one or two indexed properties (technically, anything you can access and serialize with a lambda expression is available as an index). Here is an example index using the LineOne property of the ItemViewModel. It can be referred to anywhere in code using the "LineOneIndex" name:

return new System.Collections.Generic.List<ITableDefinition>
{
    CreateTableDefinition<ItemViewModel,int>(i=>i.Id)
    .WithIndex<ItemViewModel,string,int>("LineOneIndex",i=>i.LineOne)
};

Sterling uses a higher-level serialization method for the index values (it doesn't go through the entire complex recursion and inspection uses for the main classes being serialized) so the index must not be null and must have a valid serializer (i.e. a built-in serializer or a custom one, which will be explained later). If you have a nullable column, you can still use it as the index, just define it as non-nullable and cast it to a default value. For example, if the "LineOneIndex" might have a null value (which is possible with strings) the non-null index can be defined like this:

return new System.Collections.Generic.List<ITableDefinition>
{
    CreateTableDefinition<ItemViewModel,int>(i=>i.Id)
    .WithIndex<ItemViewModel,string,int>("LineOneIndex",i=>i.LineOne ?? string.Empty)
};

Setting up Sterling for Tombstoning

Now that you've defined a database, some types and an index, it's time to integrate the database with your Windows Phone 7 application. The majority of integration will happen in the App.xaml.cs to allow for hooking into the phone tombstone events.

First, add references to the top of the class for the components that are used by the Sterling database engine:

private static ISterlingDatabaseInstance _database = null;
private static SterlingEngine _engine = null;
private static SterlingDefaultLogger _logger = null;

It is also suggested you expose a static property to make it easy to reference the database from anywhere within your application:

public static ISterlingDatabaseInstance Database
{
    get
    {
        return _database;
    }
}

Next, create two methods. The first is designed to activate the database when the application is first launched, or when the phone wakes up from a tombstone event. The second will deactivate the engine when the application is exited or tombstoned.

private void _ActivateEngine()
{
    _engine = new SterlingEngine();
    _logger = new SterlingDefaultLogger(SterlingLogLevel.Information);
    _engine.Activate();
    _database = _engine.SterlingDatabase.RegisterDatabase<ItemDatabase>();
}

private void _DeactivateEngine()
{
    _logger.Detach();
    _engine.Dispose();
    _database = null;
    _engine = null;
}

Notice the log level. This example uses the built-in Sterling logger, which simply dumps output to the debug window. Refer to the documentation for instructions on how to write our own logger and register it with the Sterling engine.

Now you can hook these events into the App.xaml.cs code behind using the phone lifecycle events:

private void Application_Launching(object sender, LaunchingEventArgs e)
{
    _ActivateEngine();
}

private void Application_Activated(object sender, ActivatedEventArgs e)
{
    _ActivateEngine();

    if (!App.ViewModel.IsDataLoaded)
    {
        App.ViewModel.LoadData();
    }
}

private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
    _DeactivateEngine();
}

private void Application_Closing(object sender, ClosingEventArgs e)
{
    _DeactivateEngine();
}
That's all that is required to have the engine ready and waiting for your application.

Example Sterling Operations

Saving Objects

Saving objects in Sterling is straightforward. As long as the type has been defined, you simply call the Save method on the database instance. New records will be automatically inserted, and existing records will be automatically updated. In this example, sampleData contains a list of ItemViewModel. The code snippet iterates the list, setting a unique identifier, and saves them. Note the flush for the keys that is called after the individual items are saved.

foreach (var item in sampleData)
{
    idx++;
    item.Id = idx;
    App.Database.Save(item);                
}   
         
App.Database.Flush();

Direct Loading

To directly load an instance, simply pass the type and the key. For example, this code will load the item with an id of 2:

var itemViewModel = App.Database.Load<ItemViewModel>(2);

Querying Keys

For filtering and sorting, can access the key list like this:

var keyList = from key in App.Database.Query<ItemViewModel, int>() 
                where key.Key > 5 
                select key;

Note the syntax: the query method is passed the type of the class and the type of key. The resulting list can be queried, sequenced, or any other operation performed that is supported by LINQ to Objects on the Windows Phone 7.

The key automatically provides a lazy-loaded reference to the entire class. The following query is similar to the previous example, but will lazily de-serialize the actual class instance instead of supplying a list of keys:

var instanceList = from key in App.Database.Query<ItemViewModel, int>()
                    where key.Key > 5
                    select key.LazyValue.Value; 

Using Indexes

Using indexes is very similar to using keys. You must specify the type of the index as well as the name of the index. In this example, the index is used to provide a list of identifiers for the item view models, sorted in the order of the text in the LineOne property:

var sortedByLineOne = from index in App.Database.Query<ItemViewModel, string, int>("LineOneIndex")
                                  orderby index.Index
                                  select index.Key;

Complex Queries

Sterling supports any type of query that is supported by LINQ to Objects. In the online documentation, there is an example query that joins two different indexes and creates a new anonymous type with the merged values - this is how it would look on the phone:

return from n in CurrentFoodDescription.Nutrients
        join nd in
            SterlingService.Current.Database.Query<NutrientDefinition, string, string, int>(
                FoodDatabase.NUTR_DEFINITION_UNITS_DESC)
            on n.NutrientDefinitionId equals nd.Key
        join nd2 in
            SterlingService.Current.Database.Query<NutrientDefinition, int, int>(
                FoodDatabase.NUTR_DEFINITION_SORT)
            on nd.Key equals nd2.Key
        orderby nd2.Index
        select new NutrientDescription
                    {
                        Amount = n.AmountPerHundredGrams,
                        Description = nd.Index.Item2,
                        UnitOfMeasure = nd.Index.Item1
                    };

Custom Serialization with Sterling

The last thing to cover with Sterling is custom serialization. Sterling has been updated to support as many types "out of the box" as possible. Support for null values, enums, base types, etc. has been baked in. Version 1.0 will also support complex object graphs as long as the nested properties are able to be serialized. Inevitably, whether because you need to manipulate the data or because you have a specific type not supported, you may need to provide a custom serializer. With Sterling this requires only two steps.

The first step is to inherit from the BaseSerializer class. One custom serializer can handle multiple types, and must implement a method to indicate which types are supported. In this example, the serializer supports a custom struct:

public class FoodSerializer : BaseSerializer  
{
    public override bool CanSerialize(Type targetType)
    {
        return targetType.Equals(typeof (NutrientDataElement));                
    }

    public override void Serialize(object target, BinaryWriter writer)
    {
        var data = (NutrientDataElement)target;
        writer.Write(data.NutrientDefinitionId);
        writer.Write(data.AmountPerHundredGrams);
    }

    public override object Deserialize(Type type, BinaryReader reader)
    {
        return new NutrientDataElement
                    {
                        NutrientDefinitionId = reader.ReadInt32(),
                        AmountPerHundredGrams = reader.ReadDouble()
                    };
    }
}

Note that the serializer indicates what type it supports, then provides the steps for serializing and de-serializing those types.

The second step is to simply register the custom serializer with the database before it is activated. That step looks like this:

_engine.SterlingDatabase.RegisterSerializer<FoodSerializer>();
_engine.Activate();

Conclusion

As you can see, Sterling is flexible, lightweight, and easy to use. The community continues to drive new features and version 1.0 will release in early 2011. Consider using Sterling to help with tombstoning and persistence of data in your upcoming Windows Phone 7 project. If you have any questions, don't hesitate to ask in our forums (that's a great place to post your success stories as well) and be sure to log any feature requests in our Issue Tracker database. Thanks!

Jeremy Likness

Friday, December 17, 2010

Jounce Part 8: Raising Property Changed

This will be a shorter bit for an interesting topic. I've noticed a few posts and discussion around raising the property changed notification. This continues to be the sore spot for MVVM. It creates quite a bit of ceremony for what is a fairly common piece of functionality that shouldn't be so hard to create.

Jounce supports all of the common ways of doing this, but there is a third that I learned from a colleague of mine (Keith Rome) that I added and think bears some attention.

The most common approach is to use the "magic string" technique (which many developers automatically cringe at) and do something like this:

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        RaisePropertyChanged("FirstName");
    }
}

The problem there of course is that it is too easy to type the property wrong. Coders who typically "cut and paste" might forget to update this and then they'll find themselves raising the wrong property.

The next iteration types the property a bit and gets rid of magic strings. It does this by allowing a lambda expression to be passed in. The expression tree is parsed and the property name is extracted. Some extra validation can even make sure it's inside a property - but there is still no guarantee you aren't raising the wrong property, you just know you are raising some property.

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        RaisePropertyChanged(()=>FirstName);
    }
}

So the third method is a little more safe. Not only does it avoid magic strings, but I can get out of the ceremony of passing a weird lambda expression, and I can guarantee it will raise the notification for the correct property. Before I talk about how this works, let me show you the code:

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName = value;
        RaisePropertyChanged();
    }
}

This will throw an exception if it's not in a setter. And it will always capture the current property. I still have to make a call, but no magic strings and we've reduced the ceremony somewhat. How does it work?

The trick is the context of execution. All of these methods have a little overhead (the string method actually has the best performance) but in most applications if the performance hit is in your setters, you've probably got worse issues farther up the stack.

Speaking of the stack, that's exactly what we use. We simply walk the stack frames up from the setter to find the name of the setter method itself. The convention for properties is set_propName so we look up where we are at and grab the propName.

Here's the code:

public virtual void RaisePropertyChanged()
{
    var frames = new System.Diagnostics.StackTrace();
    for (var i = 0; i < frames.FrameCount; i++)
    {
        var frame = frames.GetFrame(i).GetMethod() as MethodInfo;
        if (frame != null)
            if (frame.IsSpecialName && frame.Name.StartsWith("set_"))
            {
                RaisePropertyChanged(frame.Name.Substring(4));
                return;
            }
    }
    throw new InvalidOperationException("NotifyPropertyChanged() can only by invoked within a property setter.");
}

Notice once we extract the property name, we just pass it along to the method that takes a happy string.

There you have it - another way to do the deed with a little more overhead but a lot less ceremony.

Addendum

Thanks to everyone for the excellent responses. Apparently this is a method that has been tried in the past. As Sergey Barskiy pointed out, the JITter will likely inline the method and break the stack frame. Scott Bussinger offered this link as well: The Story of a Wicked Little Bug. What's interesting is that you can mark the method (and I have) to avoid inlining, and it seems the consensus is that this works except on 64-bit systems. Currently, there is not a 64-bit Silverlight runtime. However, with future releases we'll have to revisit and determine whether or not this remains an issue. Ideally, I'd love to be able to do this:

[NotifyPropertyChanged]
public string MyProperty { get; set; }

And be done with it.

Jeremy Likness

Thursday, December 16, 2010

A Silverlight MVVM Feed Reader from Scratch in 30 Minutes

I thought it might be useful for users (and potential users) of Jounce, MVVM, and Silverlight in general if I were to show the full process of building an application. When people ask me about how complicated MVVM is, I reply "it's easy when you know it and use the right framework." I hope this video illustrates that. I also find Silverlight in general to be an amazing technology because I can build a useful application so quickly. You'll see in the video I could have done it faster using short cuts, designer, etc. I am still waiting for someone to show me how to do the same thing in HTML5 in the same amount of time.

After recording this video, I noted two issues that have been corrected in the source and the example below. These would not impact the overall time but I did catch them only after I went live. Here are the fixes:

Text Update

In both FireFox and Chrome, hitting TAB won't advance from the text box properly as it does in IE, so the "GO" button never activated unless you clicked outside of the application. This is easily fixed by resetting the error state on a key press. In the XAML, I just wired a text change event:

<TextBox Grid.Column="1" Grid.Row="1" TextChanged="TextBox_TextChanged"...>

In the code-behind, I simply update the data-binding source:

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    ((TextBox)sender).GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

The view model then gets this tweak to clear the errors to enable the button (it will put the errors back if you click and there is a problem):

public string FeedUri
{
    get { return _feedUri; }
    set
    {
        _feedUri = value;
        RaisePropertyChanged(() => FeedUri);
        ClearErrors(() => FeedUri);
    }
}

Missing Summary

For some reason IE brings back summary information, but Chrome and Firefox do not from the feed. Therefore, I needed to add this little fix to avoid a null object reference exception when the summary information is missing:

Summary = ConvertHtml(item.Summary != null ? item.Summary.Text : string.Empty)

Finally, in the video I typed "encode" instead of "decode" on the http utility. These are the only changes to the source and example below from what you see in the video.

RSS Feed Reader in 30 Minutes from Jeremy Likness on Vimeo.

You can download the source code here.

The finished product is here:

Enjoy!
Jeremy Likness

Wednesday, December 15, 2010

Lessons Learned in Personal Web Page Part 3: Custom Panel and Listbox

In the third part of this series I wanted to cover the "3D Panel" effect you can see on my Projects page. While there are some terrific third-party carousel controls, I wanted to see what it takes to build one from scratch using a custom panel and a Listbox control.

Custom Listbox Carousel for Silverlight

The first step was to create the panel itself. I started by inheriting from Panel and called my new class ThreeDPanel

public class ThreeDPanel : Panel 
{
}

In my panel I decided to simplify things by fixing the size of a panel item. The entire panel will stretch to fit the available width and handle scroll bars, but the individual panels are the same size. To establish some parameters, I created a set of constants:

private const double ITEM_WIDTH = 450.0;
private const double ITEM_HEIGHT = 300.0;
private const double LEFT_ANGLE = -65.0;
private const double RIGHT_ANGLE = 65.0;
private const double MARGIN = 10.0;
private const double OFFSET = 80.0;
private const double ANIMATION_DURATION = 300.0;
private static readonly Size _itemSize = new Size(ITEM_WIDTH, ITEM_HEIGHT);
private const string ROTATION_Y = "(PlaneProjection.RotationY)";

First, I set the width and height of a panel. Next, the angle a non-selected panel should be at, depending on whether it is to the left or the right of the currently selected item. I provide a slight margin between items and a fixed offset from the edge of one angled item to the next. The animation to turn panels is controlled by the duration (milliseconds). The size is a simple fixed way to specify the size for an item based on the width and height settings, and the rotation Y is what is called "path notation" to get to the rotation angle of a plane projection: this is what we animate when the selection changes.

For this panel, I'm assuming it is hosted in a listbox. A more generic panel would accommodate any number of hosts, but I'm keeping it simple. One thing I know needs to happen is when a selection changes on the host listbox, I'll need to re-arrange all of my items to reflect the new selection. Therefore, the panel needs a hook into the selection change event of the listbox. First, a helper method to walk up the visual tree and find the listbox that is hosting the custom panel:

private ListBox GetListBox()
{
    var parent = VisualTreeHelper.GetParent(this);
    while (!(parent is ListBox) && parent != null)
    {
        parent = VisualTreeHelper.GetParent(parent);
    }
    return parent == null ? null : (ListBox) parent;
}

When you create a custom control, there are two main events to be concerned with: Measure and Arrange. The first step, the measure step, asks each child how much space it needs and attempts to figure out the total available size for the control. For example, a grid with fixed columns would tell each child "you have x and y space, how much of that do you need?" A grid with flexible space will give the child infinite space to work with, then compute the actual width based on how much space is needed.

In the arrange pass, the children have all flagged their desired sizes and the panel knows how much total space there is to work with. It now has the task of actually arranging the children - that is, based on the sizes they requested, it can ask them to render within that space and then place them on the surface. We'll tackle both of these passes for our custom control.

Because these passes happen anytime the view is resized, the measure pass is the perfect place to find our host listbox and hook into the selection event. I used a flag to track when this happens so I do it only once.

private bool _eventHooked;

protected override Size MeasureOverride(Size availableSize)
{
    // get the list box
    var listBox = GetListBox();

    // hook into selection change to recalculate whenever the user clicks on a panel
    if (!_eventHooked && listBox != null)
    {                
            listBox.SelectionChanged += (sender, e) => InvalidateArrange();
            _eventHooked = true;                
    }
}

Notice that when the selection changes, I call InvalidateArrange. This informs the visual tree that our layout is no longer valid, so we must recompute. This gives me the opportunity to measure and arrange the children again, and reposition everything based on the new selection.

To complete the measure pass, I do a few things. First, I check to see if I have any children. I also check to see if I've set up any animations for the children. If not, I'm going to iterate the list of children and create a storyboard and add that to the resources collection. I'll use these storyboards to animate transitions. Keep in mind this logic assumes that once I have children, the children do not change - obviously this won't work with lists that change, and would require a bit more code to track the change to the underlying collection so that the old animations could be removed and new ones inserted. I knew my list would be fixed once set so I didn't go down that path.

The animations are designed to run for the duration we configured earlier, and then stop. The animations are given a unique name. This name is set on the Tag property of the child element, to make it easy to find later. I hook that event and also target the plane projection. Again, I'm using a helper method to guarantee there is always a plane projection class to work with:

public static PlaneProjection WithPlaneProjection(this UIElement control)
{
    PlaneProjection projection;
    if (control.Projection == null || !(control.Projection is PlaneProjection))
    {
        projection = new PlaneProjection();
        control.Projection = projection;
    }
    else
    {
        projection = control.Projection as PlaneProjection;
    }
    return projection;
}

The animation set up only happens the first time there is a child in the list. After that, I call each child and pass in the size it has available. I measure my own override with the available size, but then there's a trick: I must let the layout engine know how much size I actually need based on my computations. You can see a simple equation I use to compute this knowing how my panels will be angled. If I wanted to be more precise, I could use trigonometry to compute the actual height needed based on the angle of rotation, but again I wanted to keep this example more simple so I guestimate and use a factor of 1.33.

Here is the rest of the measure code:

if (Children.Any() && !(from sb in Resources where sb.Value is Storyboard select sb).Any())
{
    foreach(var child in Children)
    {
        var projection = child.WithPlaneProjection();                    

        var tag = child.SetUniqueName();
        var sb = new Storyboard();
        sb.Completed += (o, e) => sb.Stop();         
        var da = new DoubleAnimation {Duration = TimeSpan.FromMilliseconds(ANIMATION_DURATION)};
                    
        Storyboard.SetTarget(da, projection);
        Storyboard.SetTargetProperty(da, new PropertyPath(ROTATION_Y));                    
                   
        sb.Children.Add(da);                 
        Resources.Add(tag, sb);
    }
}

foreach (var child in Children)
    child.Measure(_itemSize);

base.MeasureOverride(availableSize);

return new Size(OFFSET*(Children.Count - 1) + ITEM_WIDTH + (2.0*MARGIN), ITEM_HEIGHT * 1.33);

The last step is important because that is what lets the parent containers know how big this panel really is, so they can size accordingly. It's also needed by the built-in scrollviewer in the listbox to know when to show scrollbars.

The arrange step is where we start to do some real work. First, we just check the listbox and make sure there is a selected item because we position the panels based on which item is selected.

protected override Size ArrangeOverride(Size finalSize)
{
    var listBox = GetListBox();

    if (listBox == null || Children.Count < 1)
    {
        return base.ArrangeOverride(finalSize);
    }

    if (listBox.SelectedIndex < 0)
    {
        listBox.SelectedIndex = 0;
    }

    var selectedIndex = listBox.SelectedIndex;

    var top = (finalSize.Height - ITEM_HEIGHT)/2;
    var zIndex = 10000;
}

The "top" is a margin from the top to help center the panel. The zIndex will be used to ensure the selected panel is on top of the other panels. That's a little-known tip: you can use the Canvas.ZIndex property even if you are not in a canvas.

Now, we'll handle any panels to the left of the selected panel. We'll position them with an offset from the left edge of the panel. When you arrange, you provide a Rect that includes the offset from the left and top of the container, as well as the width and height of the child. Based on this, we'll tell the child to arrange itself with the Rect we pass in. This positions it on the panel. The next thing we do is set up an animation. If the child is already angled, the animation won't do anything because we're only setting the To property. If it was selected, it will animate to the desired angle and position. Because we stop the animation automatically, we want to set that final angle as well so the new property takes over when the animation is done. Finally, we increment the zIndex so successive panels are in front of the previous ones.

for (var leftCounter = 0; leftCounter < selectedIndex; leftCounter++)
{
    var left = leftCounter*OFFSET + MARGIN;
    var finalRect = new Rect(left, top, ITEM_WIDTH, ITEM_HEIGHT);
    Children[leftCounter].Arrange(finalRect);

    _AnimateTo(Children[leftCounter].GetValue(TagProperty).ToString(), LEFT_ANGLE);
    Children[leftCounter].WithPlaneProjection().SetValue(PlaneProjection.RotationYProperty, LEFT_ANGLE);
                
    Children[leftCounter].SetValue(Canvas.ZIndexProperty, zIndex);
    zIndex++;
}

The animation method takes the identifier we stored in the Tag property and uses that to look up the storyboard, then sets the animation based on the target and fires it off:

private void _AnimateTo(string sbKey, double angle)
{            
    if (!Resources.Contains(sbKey)) return;

    var sb = (Storyboard) Resources[sbKey];
    var da = (DoubleAnimation) (sb.Children[0]);
            
    if (!sb.GetCurrentState().Equals(ClockState.Stopped))
    {
        sb.Stop();
    }
    da.To = angle;           
    sb.Begin();
}

We can then do the same thing for the right panels, going from the outside in:

for (var rightCounter = Children.Count - 1; rightCounter > selectedIndex; rightCounter--)
{
    var left = rightCounter*OFFSET + MARGIN;
    var finalRect = new Rect(left, top, ITEM_WIDTH, ITEM_HEIGHT);
    Children[rightCounter].Arrange(finalRect);

    _AnimateTo(Children[rightCounter].GetValue(TagProperty).ToString(), RIGHT_ANGLE);
    Children[rightCounter].WithPlaneProjection().SetValue(PlaneProjection.RotationYProperty, RIGHT_ANGLE);
                
    Children[rightCounter].SetValue(Canvas.ZIndexProperty, zIndex);
    zIndex++;
}

The final step is to show the selected item with no rotation (we'll animate it to that position from wherever it was). This will have the highest zIndex so it appears on top. We return the size based on our final arrange call, which takes into account all of the children:

var leftMargin = selectedIndex*OFFSET + MARGIN;
Children[selectedIndex].Arrange(new Rect(leftMargin, top, ITEM_WIDTH, ITEM_HEIGHT));
_AnimateTo(Children[selectedIndex].GetValue(TagProperty).ToString(), 0d);                
Children[selectedIndex].WithPlaneProjection().SetValue(PlaneProjection.RotationYProperty, 0d);
Children[selectedIndex].SetValue(Canvas.ZIndexProperty, zIndex);

return base.ArrangeOverride(finalSize);

Now comes the fun part: wiring it up. First, I created a JSON file to easily add projects as I have new ones to share:

{
    "Projects": [
        { 
            "Title": "Sterling Object-Oriented Database for Silverlight 4 and Windows Phone 7",
            "Description": "I am the main developer and coordinator for Sterling, a lightweight object-oriented database implementation on Silverlight and Windows Phone 7 that works with your existing class structures. Sterling supports full LINQ to Object queries over keys and indexes for fast retrieval of information from large data sets.",
            "Uri" : "http://sterling.codeplex.com/"
        },
        { 
            "Title": "Microsoft 2010 Vancouver Winter Olympics",
            "Description": "In this project, I used Silverlight 3, ASP.NET, C# Windows Services and the Managed Extensibility Framework (MEF) to build an internal health monitoring system with a realtime dashboard that enabled on-demand remote configuration changes for the IIS Smooth Streaming servers that delivered the live and archived Olympic content. The system these tools supported generated over 12 petabytes of data, and at peak loads streamed 374 gigabits of video per second and handled 2.4 million pages per second. With 82 million mobile page views, 1.9 million mobile video streams, and 5,000 hours of live and on-demand video, the Olympics streaming video system was a wonderful demonstration of Microsoft technology and media platforms.",
            "Uri": "http://wintellect.com/Consulting/Case-Studies/2010-Winter-Olympic-Health-Monitoring-System"
        },
        {
            "Title": "Jounce MVVM with MEF Guidance for Silverlight Applications",
            "Description": "Jounce is a reference framework I built for Silverlight intended to provide guidance for building modular line of business applications that follow the MVVM pattern and utilize the Managed Extensibility Framework (MEF). Jounce is inspired by existing frameworks including Prism and Caliburn.Micro. Jounce is based on my real world experience building enterprise line of business modular Silverlight applications.",
            "Uri": "http://jounce.codeplex.com/"
        },
        {
            "Title": "Social Analytics and Marketing Tool",
            "Description": "Microsoft’s Looking Glass product is a Social Network Analytics and Marketing tool that is the result of a two-year effort by the Developer and Platform Evangelist (DPE) team. For this project I was brought in to redesign the architecture of the Silverlight client to support Silverlight 4 and the MVVM pattern. The code was also migrated fully to Silverlight 4. Considerations for this part of the effort included provision of a core framework that addresses the MVVM implementation, modularity, and extensibility using the Managed Extensibility Framework (MEF), isolated storage for caching where performance benefits were identified, and full unit testing using the Silverlight Unit Testing Framework.",
            "Uri": "http://wintellect.com/Consulting/Case-Studies/Looking-Glass"
        },
        { 
            "Title": "Silverlight SharePoint Business Application Proof of Concept",
            "Description": "I worked with Microsoft Corp. and PricewaterhouseCoopers using Silverlight 4 to build integrated SharePoint web parts for a SharePoint-based line of business application. Taking advantage of the SharePoint 2010 new client object model, we developed rich, interactive web parts that integrated directly to SharePoint lists. This included design-time aware Silverlight controls that could be easily styled and manipulated in Microsoft Expression Blend via sample data automatically generated when the controls are in design mode. The Collaborative Application Markup Language (CAML) was used to query the SharePoint lists to interact directly with SharePoint data.",
            "Uri": "http://wintellect.com/Consulting/Case-Studies/Integrating-SharePoint-Silverlight-Web-Parts"
        },
        {
            "Title": "MEF Contrib",
            "Description": "MefContrib is a community-developed set of extensions, tools and samples for the Managed Extensibility Framework (MEF). The project is an open source project, licensed under the MS-PL license. I provide quick start articles and video tutorials for this project.",
            "Uri": "http://mefcontrib.com/"
        }

    ]
}

Next, the view model. It does two things: first, it fetches the list of projects and parses them from JSON into a list of classes that can be bound to the list. Second, it exposes a command for paging through the projects. It takes a parameter to specify if it bound for forward or backward and will advanced the selected index until it hits the end (or vice versa).

The external command is passed to the individual project items, and is used to open a new web page where the project or case study exists.

[ExportAsViewModel(Constants.VIEWMODEL_PROJECTS)]
public class ProjectsViewModel : BaseViewModel
{
    private const string PROJECT_TEXT = "Projects.js";

    public ObservableCollection<ProjectItem> Projects { get; private set; }        

    private ProjectItem _currentItem;
    public ProjectItem CurrentItem
    {
        get { return _currentItem; }
        set
        {
            _currentItem = value;
            RaisePropertyChanged(() => CurrentItem);
            NavigateProject.RaiseCanExecuteChanged();
        }
    }

    public IActionCommand<bool> NavigateProject { get; private set; }

    public IActionCommand<Uri> NavigateExternalProject { get; private set; }

    /// 
    ///     Constructor checks for design time or initiates download of items
    /// 
    public ProjectsViewModel()
    {
        Projects = new ObservableCollection<ProjectItem>();

        NavigateProject = new ActionCommand<bool>(_Navigate, _CanNavigate);

        NavigateExternalProject = new ActionCommand<Uri>(_NavigateExternal, _CanNavigateExternal);

        if (InDesigner)
        {
            Projects.Add(new ProjectItem
                                {
                                    Title = "Sample Project",
                                    Description = "This is a sample project that I worked on."
                                });
            Projects.Add(new ProjectItem
            {
                Title = "Yet Another Project with a Much Longer Title",
                Description = "This is another project that I worked on."
            });
        }
        else
        {
            HelperExtensions.LoadResource(new Uri(PROJECT_TEXT, UriKind.Relative), ParseProjects);                
        }
    }

    private static bool _CanNavigateExternal(Uri arg)
    {
        return arg == null ? false : !arg.Equals(HtmlPage.Document.DocumentUri);
    }

    private static void _NavigateExternal(Uri target)
    {
        HtmlPage.Window.Navigate(target, "_blank");
    }

    private bool _CanNavigate(bool forward)
    {
        if (Projects.Count == 0)
        {
            return false;
        }
            
        return forward 
                    ? _currentItem != Projects[Projects.Count - 1]
                    : _currentItem != Projects[0];
    }

    private void _Navigate(bool forward)
    {
        if (!_CanNavigate(forward)) return;

        var curIndex = Projects.IndexOf(_currentItem);
        CurrentItem = forward ? Projects[curIndex + 1] : Projects[curIndex - 1];
    }

    private void ParseProjects(string result)
    {
        const string PROJECTS = "Projects";
        const string TITLE = "Title";
        const string DESCRIPTION = "Description";
        const string URI = "Uri";     
            
        if (string.IsNullOrEmpty(result))
        {
            return;
        }

        var json = JsonValue.Parse(result);

        var projects = json[PROJECTS] as JsonArray;
        if (projects == null) return;
        foreach (var projectItem in projects.Select(project => new ProjectItem
                                                                    {
                                                                        Title = project[TITLE],
                                                                        Description = project[DESCRIPTION],
                                                                        NavigationUri = project.ContainsKey(URI) ? new Uri(project[URI],UriKind.Absolute) :
                                                                        HtmlPage.Document.DocumentUri,
                                                                        NavigationCommand = NavigateExternalProject
                                                                    }))
        {
            Projects.Add(projectItem);
        }
    }        
}

Now we just need to bind the XAML. First, I'll define the "true/false" parameters for the command buttons to navigate the project:

<Grid.Resources>
    <System:Boolean x:Key="True">true</System:Boolean>
    <System:Boolean x:Key="False">false</System:Boolean>
</Grid.Resources>

Next is the buttons, bound to the forward/backward respectively:

<StackPanel Grid.Row="1" Margin="3" HorizontalAlignment="Center" VerticalAlignment="Center"
            Orientation="Horizontal">
    <Button Content=" &lt; " Style="{StaticResource BlueButton}" Command="{Binding NavigateProject}"
            CommandParameter="{Binding Source={StaticResource False}}"/>                           
    <Button Content=" &gt; " Style="{StaticResource BlueButton}" Margin="3,0,0,0"
            Command="{Binding NavigateProject}" CommandParameter="{Binding Source={StaticResource True}}"/>
</StackPanel>

Finally, the listbox. Note we are using the built-in features that will highlight a panel when we hover, highlight the selected panel, and also swap the selected item when one is clicked. These services are provided by the listbox control, while the layout and view is arranged by our custom panel that we specify in the ItemsPanel section. The DataTemplate just contains the grid/button that goes inside each panel. Here is the listbox definition:

<ListBox Margin="5" HorizontalAlignment="Center" VerticalAlignment="Top"          
    Background="{x:Null}"
    Grid.Row="2" ItemsSource="{Binding Projects}"
            SelectedItem="{Binding CurrentItem,Mode=TwoWay}">    
<ListBox.ItemsPanel>
    <ItemsPanelTemplate>
        <Controls:ThreeDPanel/>
    </ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
        <Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
        <Setter Property="VerticalContentAlignment" Value="Stretch"></Setter>
    </Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
        <DataTemplate>...panel content...        
        </DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Notice the inline style that targets a listbox item. By default, the items will not stretch to fill the container. This can create some issues and headaches with layout. Adding this style implicitly styles the listbox items so they do stretch and fill the parent container. This allows the grid we define in our data template to properly size itself within the individual panel.

And that's it - you've now learned how to create a custom panel and use it inside a listbox to create a custom carousel-style effect. I'm sure from here you can imagine the steps to center the panel, make a reflection, etc. that ultimately leads to full blown carousel controls. You should also realize by now that almost any type of UI paradigm can be created simply by styling the listbox.

Jeremy Likness

Saturday, December 11, 2010

Lessons Learned in Personal Web Page Part 2: Enter the Matrix

This is the second part in the post of more "fun" effects and challenges I worked through in Silverlight when building my personal web page.

One effect I really wanted to work out is the Matrix-style effect you can see on my blog page. After looking at the video I determined the main characters seemed to remain in a fixed position and it's the shading of them that creates the motion. I was able to create the effect using the MVVM model (I wasn't trying to force this, it just happened naturally) and storyboards - there is no timer or code behind involved as it is all done with XAML, data-binding, and the defined behaviors that create the storyboards.

Working from the bottom up, I first wanted to tackle having some random, funky characters. I knew that I would be handling "columns" of these, so I started with what a column would look like. The MatrixText class takes the size of the column and generates the characters for me, exposing them as a list of strings. I inherited from observable collection in case I wanted to manipulate the strings:

public class MatrixText : ObservableCollection<string>
{     
    private static readonly Random _random = new Random();        

    public Guid Id { get; private set; }

    public MatrixText() 
    {
        Id = Guid.NewGuid();
    }

    public MatrixText(int size) : this()
    {
        var bytes = new byte[size];
        _random.NextBytes(bytes);
        foreach(var c in bytes)
        {
            Add(new string((char)c, 1));
        }
    }

    public override bool Equals(object obj)
    {
        return obj is MatrixText && ((MatrixText) obj).Id.Equals(Id);
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

Next, I created the MatrixStack class to hold any number of MatrixText to complete the grid. It also takes a constructor with the number of items, and then simply spins up a list. Keep in mind each element is automatically generating characters:

public class MatrixStack : ObservableCollection<MatrixText>
{
    public Guid Id { get; private set; }       

    public MatrixStack() 
    {
        Id = Guid.NewGuid();
    }

    public MatrixStack(int size, int textSize) : this()
    {
        var remaining = size;
        while (remaining-- > 0)
        {
            Add(new MatrixText(textSize));
        }
    }

    public override bool Equals(object obj)
    {
        return obj is MatrixStack && ((MatrixStack) obj).Id.Equals(Id);
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

So now I can easily create the matrix of characters with a single class. Now I could build the view model, which exposes the matrix stack:

[ExportAsViewModel(Constants.VIEWMODEL_BLOG)]
public class BlogViewModel : BaseViewModel 
{
    private const string CSHARPER = "http://feeds.feedburner.com/csharperimage";

    public BlogViewModel()
    {
        Stack = InDesigner ? new MatrixStack(40, 20) : new MatrixStack(76, 70);
        Blog = new ObservableCollection<BlogEntryItem>();
        NavigateExternalProject = new ActionCommand<Uri>(_NavigateExternal);

        if (InDesigner)
        {
            Blog.Add(new BlogEntryItem
                            {
                                Link = new Uri("http://www.bing.com/", UriKind.Absolute),
                                Title = "This is a Sample Blog Entry of Mine",
                                Posted = DateTime.Now                                 
                            }
                );
            Blog.Add(new BlogEntryItem
                            {
                                Link = new Uri("http://www.bing.com/", UriKind.Absolute),
                                Title = "This is a Another Sample Blog Entry of Mine",
                                Posted = DateTime.Now                                 
                            }
                );
        }
        else
        {
            HelperExtensions.LoadResource(new Uri(CSHARPER, UriKind.Absolute), ParseBlog);  
        }
    }

    private static void _NavigateExternal(Uri target)
    {
        HtmlPage.Window.Navigate(target, "_blank");
    }

    public IActionCommand<Uri> NavigateExternalProject { get; private set; }

    private void ParseBlog(string feedSrc)
    {
        if (string.IsNullOrEmpty(feedSrc))
        {
            return;
        }

        try
        {
            var reader = XmlReader.Create(new StringReader(feedSrc));
            var feed = SyndicationFeed.Load(reader);
            JounceHelper.ExecuteOnUI(()=>{
            foreach (var feedItem in feed.Items)
            {
                Blog.Add(new BlogEntryItem
                                {
                                    Link = feedItem.Links[0].GetAbsoluteUri(),
                                    Posted = feedItem.PublishDate.Date,
                                    Title = feedItem.Title.Text,
                                    NavigateCommand = NavigateExternalProject
                                });
            }
            });           
        }
        catch(Exception ex)
        {
            Debug.WriteLine(ex.ToString());
        }
    }

    public MatrixStack Stack { get; private set; }

    public ObservableCollection<BlogEntryItem> Blog { get; private set; }
}

There are a few moving pieces here. The matrix is created and sized based on whether or not we are in the designer (a smaller matrix will be created at design time). I also have the list of blog entries and either populate these with sample data or use the extension helper I introduced in the last post to fetch the feed and parse it using the SyndicationFeed class.

That's it for the view model - it provides all of the binding I need to create the effect. It also has all of the code to download and parse the RSS feed from my blog.

The next thing I did was get the characters on the screen using binding. I used a combination of items controls, one for each column and another for each character:

<ItemsControl Margin="100 0 0 0" Opacity="0.6" ItemsSource="{Binding Stack}"> 
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <ItemsControl ItemsSource="{Binding}">
                    <Interactivity:Interaction.Behaviors>
                        <Behaviors:FreezeBehavior/>
                    </Interactivity:Interaction.Behaviors>
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Vertical"/>                                          
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding}" FontFamily="Consolas" FontSize="16" FontWeight="Bold" Foreground="LawnGreen"/>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
                <Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
                            Margin="0 -800 0 0"
                            Fill="Black">
                    <Rectangle.RenderTransform>
                        <ScaleTransform ScaleY="2.0"/>
                    </Rectangle.RenderTransform>
                    <Rectangle.OpacityMask>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                            <GradientStop Offset="0" Color="#FF000000"/>
                            <GradientStop Offset="0.3" Color="#FF000000"/>
                            <GradientStop Offset="0.6" Color="#00000000"/>
                            <GradientStop Offset="0.6001" Color="#FF000000"/>
                            <GradientStop Offset="1.0" Color="#FF000000"/>
                        </LinearGradientBrush>
                    </Rectangle.OpacityMask>
                    <Interactivity:Interaction.Behaviors>
                        <Behaviors:MatrixMotionBehavior Height="600"/>
                    </Interactivity:Interaction.Behaviors>
                </Rectangle>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

One thing that may jump out is a few behaviors. We'll look at these in a second. Essentially, the binding will create our matrix of columns and rows of characters. The key to the effect is the rectangle in each column. You'll notice it has an opacity mask and a negative margin. This allows me to mask the intensity of green on the characters because the rectangle overlays them. The negative margin starts the rectangle over the top of the page so I can bring it down to cover the characters. In order to have different characters "streaming" at different speeds, I simply offset the vertical position of the rectangle using a storyboard run at different speeds. Here is the behavior:

public class MatrixMotionBehavior : Behavior<FrameworkElement>
{
    private static readonly Random _random = new Random();

    public int Height { get; set; }

    protected override void OnAttached()
    {
        var storyboard = new Storyboard();
        var sbName = HelperExtensions.UniqueName();
        storyboard.SetValue(FrameworkElement.NameProperty, sbName);

        var da = new DoubleAnimation
                        {
                            Duration = TimeSpan.FromSeconds(1.0 + _random.NextDouble()*8.0)
                        };

        da.SetValue(FrameworkElement.NameProperty, HelperExtensions.UniqueName());

        var translateTransform = AssociatedObject.WithTransform<TranslateTransform>();
        Storyboard.SetTarget(da, translateTransform);
        Storyboard.SetTargetProperty(da, new PropertyPath("(TranslateTransform.Y)"));

        da.RepeatBehavior = RepeatBehavior.Forever;
        da.From = -1*Height;
        da.To = Height*2;

        storyboard.Children.Add(da);
        AssociatedObject.Resources.Add(sbName, storyboard);
        storyboard.Begin();
    }
}

The behavior takes whatever it is attached to and creates a storyboard that moves the Y offset using a translate transform. It will animate through whatever range is specified by the height. I knew I'd tweak this in XAML so I didn't make it a dependency property, but it could be for binding. I have to give the storyboards a unique name and add them to the resources of the container before I can launch them.

To grab the translate transform, I use a helper method I created that always ensures I have a valid transformation to work with. It is non-destructive, so if there are other transformations, it will work nicely with them. For example, if there is a scale transform, this helper extension with create a transformation group, move the scale transform into the group, then add the translation transform. You can close this generic extension with any transformation:

public static T WithTransform<T>(this UIElement control) where T: Transform, new() 
{
    T transform;

    if (control.RenderTransform == null)
    {
        transform = new T();
        control.RenderTransform = transform;
    }
    else if (control.RenderTransform is TranslateTransform)
    {
        transform = (T) control.RenderTransform;
    }
    else if (control.RenderTransform is TransformGroup)
    {
        var g = (TransformGroup) control.RenderTransform;
        transform = (from t in g.Children where t is T select (T)t).FirstOrDefault();
        if (transform == null)
        {
            transform = new T();
            g.Children.Add(transform);
        }
    }
    else
    {
        var g = new TransformGroup();
        var temp = control.RenderTransform;
        control.RenderTransform = g;
        g.Children.Add(temp);
        transform = new T();
        g.Children.Add(transform);
    }

    return transform;
}

I also have an extension to ensure unique names:

public static string SetUniqueName(this UIElement control)
{
    var guid = UniqueName();
    control.SetValue(FrameworkElement.TagProperty, guid);
    return guid;
}

public static string UniqueName()
{
    return Guid.NewGuid().ToString().Replace("-", string.Empty);            
}

At this point we have a fairly serviceable effect, with two problems. First, the performance is less than par because the render engine has to manage every individual TextBlock on the page. The second issue is that without clipping, the rectangle ends up masking other areas of the page that wouldn't be affected. To fix the clip, I used a behavior that automatically creates a clip the size of the container it is bound to:

public class Clip
{
    public static bool GetToBounds(DependencyObject depObj)
    {
        return (bool)depObj.GetValue(ToBoundsProperty);
    }

    public static void SetToBounds(DependencyObject depObj, bool clipToBounds)
    {
        depObj.SetValue(ToBoundsProperty, clipToBounds);
    }

    public static readonly DependencyProperty ToBoundsProperty =
    DependencyProperty.RegisterAttached("ToBounds", typeof(bool),
    typeof(Clip), new PropertyMetadata(false, OnToBoundsPropertyChanged));

    private static void OnToBoundsPropertyChanged(DependencyObject d,
    DependencyPropertyChangedEventArgs e)
    {
        var fe = d as FrameworkElement;
        if (fe != null)
        {
            ClipToBounds(fe);
            fe.Loaded += FeLoaded;
            fe.SizeChanged += FeSizeChanged;
        }
    }

    private static void ClipToBounds(FrameworkElement fe)
    {
        if (GetToBounds(fe))
        {
            fe.Clip = new RectangleGeometry
                            {
                Rect = new Rect(0, 0, fe.ActualWidth, fe.ActualHeight)
            };
        }
        else
        {
            fe.Clip = null;
        }
    }

    static void FeSizeChanged(object sender, SizeChangedEventArgs e)
    {
        ClipToBounds(sender as FrameworkElement);
    }

    static void FeLoaded(object sender, RoutedEventArgs e)
    {
        ClipToBounds(sender as FrameworkElement);
    }
}

Attaching this creates a clip the size of the containing grid, so when the rectangles are off the top of the screen, they don't overlay the header of the page.

Next, to keep from having to re-render every character, I decided to capture each column as an image. The columns remain static and it is only the mask that moves (I could probably have extended this to make a single image of the entire frame as well). This considerably improved performance although it is still takes significant CPU to recalculate the masks each frame. The freeze behavior can attach to any UIElement inside a grid. It will automatically render the element to an image, then swap the element with the snapshot of the element:

public class FreezeBehavior : Behavior<FrameworkElement>
{
    protected override void OnAttached()
    {
        AssociatedObject.Loaded += AssociatedObject_Loaded;
    }

    void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
    {
        var parent = VisualTreeHelper.GetParent(AssociatedObject) as Panel;

        if (parent == null)
        {
            return;
        }

        var bitmap = new WriteableBitmap(AssociatedObject, new TranslateTransform());
        bitmap.Invalidate();

        var image = new Image {Width = bitmap.PixelWidth, Height = bitmap.PixelHeight, Source = bitmap};
        image.SetUniqueName();

        var pos = parent.Children.IndexOf(AssociatedObject);
        parent.Children.Remove(AssociatedObject);
        parent.Children.Insert(pos, image);            
            
        return;
    }        
}

While there may be some other ways to tweak this, I was very satisfied with the end result. By taking the challenge and breaking it into small, specific steps I was able to create the effect fairly closely to how I intended and then overlay the blog entries on top of it.

In the next post for this series, I'll tackle the more practical task of creating a custom panel to use in a list box for a carousel-like effect.

The source for this project is available from my home page: JeremyLikness.com.

Jeremy Likness

Thursday, December 9, 2010

Lessons Learned in Personal Web Page Part 1: Dynamic XAML

Recently I undertook the effort to rewrite my personal web page for a few reasons. I always like side projects for learning so I wanted to explore some "fun" ideas such as three dimensional objects and plasma (I blogged about those here) but also some more practical ideas like dynamic content and custom list boxes. In future posts with this series I'll be covering:

  • "The Matrix Effect" from my Blog tab
  • The custom panel I used for the list box in my Projects tab
  • Fun with physics in my Twitter tab

The full source for the personal website is available on the home page, so just click the link to download. You'll also need to install the Farseer Physics Engine and the Physics Helper.

I used Jounce as the main framework for this and integrated it with the Silverlight navigation framework.

The first thing I'll cover is some of the dynamic content. I wanted to be able to edit certain entries and update them easily without re-publishing the entire XAP file. The Biography page is an example of this: the content is loaded in a separate file so I can edit and uploaded without changing the XAP.

The External File

The external file is easy: I create an XML document that contains the right namespace declarations and the content I want to show. In this case, I'm using a RichTextBox but I could use any type of root level control I wanted. Obviously, an approach like this would require some extra measures in production applications due to the security risk (you lose control over what that external content may contain).

Here is a snippet of the file I dropped into ClientBin to make it easy to access by Silverlight:

<RichTextBox xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  IsReadOnly="True" Background="{x:Null}">
<Paragraph FontWeight="Bold">
  Summary<LineBreak/>
</Paragraph>
 <Paragraph>
  I am a Silverlight-focused developer/architect and technical project manager with sales and entrepreneurial experience, a passion for mentoring and public speaking, 
  and a strong social media presence.
<LineBreak/>
</Paragraph>
</RichTextBox>

The View Model

The view model is responsible for fetching and displaying the text. I take away the concern of the UI rendering and just deal with the XAML as a string. Because I want to be friendly inside of the designer, I also supply some values when in design mode (the Jounce base view model provides an InDesigner bool for this).

Here is the entire view model:

namespace JeremyLiknessSL.ViewModels
{
    [ExportAsViewModel(Constants.VIEWMODEL_BIO)]
    public class BioViewModel : BaseViewModel 
    {
        private const string SAMPLE_XAML = @"<RichTextBox xmlns=""http://schemas.microsoft.com/client/2007"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"" "
                              +
                              @" IsReadOnly=""True"" Background=""{x:Null}""><Paragraph>This is a test text box.</Paragraph></RichTextBox>";
        private const string BIO_XAML = "Bio.xml";
                
        public BioViewModel()
        {
            if (InDesigner)
            {
                Text = SAMPLE_XAML;
            }
            else
            {                
                HelperExtensions.LoadResource(new Uri(BIO_XAML,UriKind.Relative), s => Text = s);
            }
        }

        private string _text;
        public string Text
        {
            get { return _text; }
            set
            {
                _text = value;
                RaisePropertyChanged(()=>Text);
            }
        }
    }
}

That looks simple, no? My "helper" method is designed to allow me to load any string resource from the web. In this case, I just pass a delegate that will receive the text when ready and do something with it. I silently absorb errors but of course would propagate those in a business application.

Here is the helper set of methods:

public static void LoadResource(Uri resource, Action<string> resourceLoaded)
{
    var wc = new WebClient();
    wc.DownloadStringCompleted += WcDownloadStringCompleted;
    wc.DownloadStringAsync(resource, resourceLoaded);
}

static void WcDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    var wc = sender as WebClient;
    var callback = e.UserState as Action<string>;
    if (wc == null || callback == null)
    {
        if (callback != null)
        {
            callback(string.Empty);
        }
        return;
    }
    wc.DownloadStringCompleted -= WcDownloadStringCompleted;
    callback(e.Result);
}

Notice I wire into the call and pass the callback as part of the state. Upon the return, I get my call back refrence back and either call back with nothing if there was an error, or with the actual result if it procesed correctly.

The Dynamic XAML

Next, I need to have a control I can bind the text to and have it render an actual XAML control. I decided to create a user control which contains a Grid surface to host the control:

<UserControl x:Class="JeremyLiknessSL.Controls.DynamicXaml"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">    
    <Grid x:Name="LayoutRoot"/>
</UserControl>

In the code-behind I'll expose a dependency property to bind to. Whenever the dependency property changes, the code-behind will attempt to load that control from the XAML and inject it into the grid. The code-behind looks like this:

public partial class DynamicXaml
{
    public DynamicXaml()
    {
        InitializeComponent();
    }

    public string Xaml 
    {
        get { return GetXaml(this); }
        set { SetXaml(this, value); }
    }

    public static DependencyProperty XamlProperty =
        DependencyProperty.Register("Xaml",
                                    typeof(string),
                                    typeof(DynamicXaml),
                                    new PropertyMetadata(null, _SetXaml));

    private static void _SetXaml(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((DynamicXaml)d).LayoutRoot.Children.Clear();
        var uiElement = XamlReader.Load(e.NewValue.ToString()) as UIElement;
        if (uiElement == null) return;
        uiElement.SetValue(NameProperty, Guid.NewGuid().ToString().Replace("-", string.Empty));
        ((DynamicXaml)d).LayoutRoot.Children.Add(uiElement);
    }

    public static string GetXaml(DependencyObject obj)
    {
        return obj.GetValue(XamlProperty).ToString();
    }

    public static void SetXaml(DependencyObject obj, string value)
    {
        obj.SetValue(XamlProperty, value);
    }
}

Now, with the control in place, I can databind to my view model in the main view. The binding in the biography view looks like this:

<UserControl...>
<Controls:DynamicXaml Grid.Row="1" Xaml="{Binding Text}"/>
</UserControl>

As you can see, very straightforward. My view model also supplies a nice "default" control in design time, so I can see the result in the Visual Studio and Blend designers without having to run the application. This means we have a design-time friendly, MVVM-friendly implemenation of dynamic runtime controls. It also means I can simply edit one file when my biography needs to be updated and push it out to reflect the latest information.

Jeremy Likness

Saturday, December 4, 2010

Old School Silverlight Effects

This weekend I was working a little bit on my personal web site (rewriting it again, to use some better themes and my Jounce framework) and decided it would be fun to tinker with some old school effects. I decided to focus on two specific things: a transparent three-dimensional cube, and a plasma effect.

You can see the results here (mouse over the cube to control the spin rate and direction):


So how's it done? Of course, full source code is available.

First, I knew I could handle the cube using plane projections. It turns out that this project had a decent example using images. I simply refactored it to use filled rectangles instead.

The XAML defines the six sides of the cube:

<UserControl x:Class="OldSkool.C64.Cube"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">       
        <Grid x:Name="LayoutRoot" Height="250" Width="490">
        <Grid.Resources>
            <Style TargetType="Rectangle">
                <Setter Property="Margin" Value="170,50"/>
                <Setter Property="Height" Value="150"/>
                <Setter Property="Width" Value="150"/>
                <Setter Property="Opacity" Value="0.5"/>
            </Style>
        </Grid.Resources>
            <Rectangle Fill="Red">
                <Rectangle.Projection>
                    <PlaneProjection x:Name="Rectangle1Projection" CenterOfRotationZ="-75" RotationX="-180"/>
                </Rectangle.Projection>
            </Rectangle>
        <Rectangle Fill="Green">
            <Rectangle.Projection>
                <PlaneProjection x:Name="Rectangle2Projection" CenterOfRotationZ="-75" RotationX="-90"/>
            </Rectangle.Projection>
        </Rectangle><!-- etc. -->

Four sides are defined this way, then two sides must not only be projected, but also rotated to keep the sides of the square flush:

<!-- ...continued -->
        <Grid Margin="170,50">
            <Grid.Projection>
                <PlaneProjection x:Name="Rectangle5Projection" CenterOfRotationZ="-75" RotationY="-90"/>
            </Grid.Projection>
            <Rectangle Margin="0" Fill="Yellow" RenderTransformOrigin="0.5,0.5">
                <Rectangle.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform x:Name="Rectangle5Rotation" Angle="0"/>
                        <TranslateTransform/>
                    </TransformGroup>
                </Rectangle.RenderTransform>
            </Rectangle>
        </Grid><!-- etc. -->
    </Grid>  
</UserControl>

Now in the code behind it gets more interesting.

public partial class Cube
{
    private Point _pt;
        
    public Cube()
    {
        InitializeComponent();
        Loaded += Cube_Loaded;
    }

    void Cube_Loaded(object sender, RoutedEventArgs e)
    {
        Loaded -= Cube_Loaded;
        if (!DesignerProperties.IsInDesignTool)
        {
            LayoutRoot.MouseMove += LayoutRoot_MouseMove;
        }
        CompositionTarget.Rendering += CompositionTarget_Rendering;
        
    }
        
    void CompositionTarget_Rendering(object sender, EventArgs e)
    {        
        Rectangle1Projection.RotationY += ((_pt.X - (LayoutRoot.ActualWidth / 2)) / LayoutRoot.ActualWidth) * 10;
        Rectangle2Projection.RotationY += ((_pt.X - (LayoutRoot.ActualWidth / 2)) / LayoutRoot.ActualWidth) * 10;
        Rectangle3Projection.RotationY += ((_pt.X - (LayoutRoot.ActualWidth / 2)) / LayoutRoot.ActualWidth) * 10;
        Rectangle4Projection.RotationY += ((_pt.X - (LayoutRoot.ActualWidth / 2)) / LayoutRoot.ActualWidth) * 10;
        Rectangle5Projection.RotationY += ((_pt.X - (LayoutRoot.ActualWidth / 2)) / LayoutRoot.ActualWidth) * 10;
        Rectangle6Projection.RotationY += ((_pt.X - (LayoutRoot.ActualWidth / 2)) / LayoutRoot.ActualWidth) * 10;
        Rectangle1Projection.RotationX += ((_pt.Y - (LayoutRoot.ActualHeight / 2)) / LayoutRoot.ActualHeight) * 10;
        Rectangle2Projection.RotationX += ((_pt.Y - (LayoutRoot.ActualHeight / 2)) / LayoutRoot.ActualHeight) * 10;
        Rectangle3Projection.RotationX += ((_pt.Y - (LayoutRoot.ActualHeight / 2)) / LayoutRoot.ActualHeight) * 10;
        Rectangle4Projection.RotationX += ((_pt.Y - (LayoutRoot.ActualHeight / 2)) / LayoutRoot.ActualHeight) * 10;
        Rectangle5Rotation.Angle -= ((_pt.Y - (LayoutRoot.ActualHeight / 2)) / LayoutRoot.ActualHeight) * 10;
        Rectangle6Rotation.Angle += ((_pt.Y - (LayoutRoot.ActualHeight / 2)) / LayoutRoot.ActualHeight) * 10;            

        if (DesignerProperties.IsInDesignTool)
        {
            CompositionTarget.Rendering -= CompositionTarget_Rendering;
        }
    }

    void LayoutRoot_MouseMove(object sender, MouseEventArgs e)
    {
        _pt = e.GetPosition(LayoutRoot);
    }        
}

Once it is loaded, we start to plot the mouse to help control the cube. In addition, we latch into the Silverlight rendering cycle. Normally for controlled animations we'd use something like a DispatcherTimer to be consistent across platforms, as this will run faster or slower on your machine based on your frame rate. Once we pop into a frame, it's fairly simple: we spin the cube slightly in a vector towards the mouse pointer.

That's all there is too it. It's not true 3D, but by using a trick that lets us project the sides around a common point we give you the pretty convincing illusion of a three dimensional cube (if you have a good eye, you'll spot some iterations that warp the perspective slightly due to the way it's done).

So the cube is great but I wanted something more funky to put it on. Let's go to plasma. Plasma is an effect used by creating a "frequency" of noise combined with a sine function. This, when plotted, makes for some interesting color cycling.

Instead of trying to throw it all together from scratch, I grabbed the open source Demo Effects library that is written in C++ and converted it over. First, we hold a few tables such as our palette, our sine function, and offsets into the function to cycle it:

private const int SCREEN_HEIGHT = 200;
private const int SCREEN_WIDTH = 320;

private ushort _pos1, _pos3, _tpos1, _tpos2, _tpos3, _tpos4;
private readonly int[] _aSin = new int[512];
private readonly Color[] _palette = new Color[256];

Notice I'm rendering to a very small buffer to preserve some CPU cycles. We'll put it in an image that stretches to fill and that's how it ends up taking the entire window.

Here's the sine table. Tweaking the variables will create slightly different plasma effects:

private void _CreateSineTable()
{
    for (var i = 0; i < 512; i++)
    {
        var rad = (i * 0.703125) * 0.0174532;         
        _aSin[i] = (int)(Math.Sin(rad) * 1024);     }
}

A simple algorithm to generate a "hot" palette:

private void _CreatePalette()
{
    for (var i = 0; i < 64; ++i)
    {                
        var r = i << 2;
        var g = 255 - ((i << 2) + 1);
        _palette[i] = Color.FromArgb(255, (byte)r, (byte)g, 0);
        g = (i << 2) + 1;
        _palette[i + 64] = Color.FromArgb(255, 255, (byte)g, 0);
        r = 255 - ((i << 2) + 1);
        g = 255 - ((i << 2) + 1);
        _palette[i + 128] = Color.FromArgb(255, (byte)r, (byte)g, 0);
        g = (i << 2) + 1;
        _palette[i + 192] = Color.FromArgb(255, 0, (byte) g, 0);
    } 
}

Then, every frame, I process the iterations, look up my palette value and write it out for each pixel, and finally update the image source:

void CompositionTarget_Rendering(object sender, EventArgs e)
{     
    var final = new WriteableBitmap(SCREEN_WIDTH, SCREEN_HEIGHT);

    _tpos4 = 0;
    _tpos3 = _pos3;
            
    for (var i = 0; i < SCREEN_HEIGHT; ++i)
    {
        _tpos1 = (ushort)(_pos1 + 5);
        _tpos2 = 3;

        _tpos3 &= 511;
        _tpos4 &= 511;

        for (var j = 0; j < SCREEN_WIDTH; ++j)
        {
            _tpos1 &= 511;
            _tpos2 &= 511;

            var x = _aSin[_tpos1] + _aSin[_tpos2] + _aSin[_tpos3] + _aSin[_tpos4]; 
    
            var index = (byte)(128 + (x >> 4)); 

            var c = _palette[index];

            final.Pixels[i * SCREEN_WIDTH + j] = c.A << 24 | c.R << 16 | c.G << 8 | c.B;                  

            _tpos1 += 5;
            _tpos2 += 3;
        }

        _tpos4 += 3;
        _tpos3 += 1;
    }

    _pos1 += 9;
    _pos3 += 8;

    PlasmaImage.Source = final;

    if (DesignerProperties.IsInDesignTool)
    {
        CompositionTarget.Rendering -= CompositionTarget_Rendering;
    }
}

Notice the last bit - unhooking the event. For both the cube and the plasma I'll do this. Why? Because that way I can do one pass of rendering, then stop, so I'm not killing Visual Studio during design time, but can still see the effect in the designer.

With those two controls done, it just required a little bit of stacking. I placed the plasma first, then stuck the cube in a view box set to stretch to the available dimensions, and you see the effect above.

I'm very excited to see what these types of routines will look like with the new APIs for graphics and 3D rendering in Silverlight 5!

Download the source code.

Jeremy Likness