CodeProject, MVVM, Silverlight, WPF

WPF : Design Time Data

If you are using WPF, you are more than likely using Expression BLEND to. You may even be one of the lucky ones and have a designer as part of your team.

If you do you may be aware of the whole designer/developer workflow and just how important this has become.

The idea being that XAML/Binding (and MVVM I would say) have now allowed a designer to work on the same project as the developer. Whilst the developer may be coding away the designer can be polishing up the UI to look fantastic. This is all cool.

But sometimes the designer will need some help in certain areas. Design time data is one of these areas. This is especially useful when the designer is trying to create DataTemplates that may be applied to a List of items. Without design time data the designer simply does not know what their design efforts will yield at runtime.

It is true to say that there has been a lot of work around this area done in Visual Studio 2010, but I have always used a dead simple approach.

There are many different ways, for example you could have something like a ViewModelLocator which stores view types against some other type. The other type could be an actual ViewModel or some design time mock object.

You could use MEF as Glenn Block and John Papa talk about in this post:

http://johnpapa.net/silverlight/simple-viewmodel-locator-for-mvvm-the-patients-have-left-the-asylum/

For me I like to keep it simple.

Here is how I do it.

I have 2 attached DPs, one for the real ViewModel and one for the design time ViewModel. It is dead simple if you are in design time use the design time one, if you are not use the actual one.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace ViewFirstDesignTimeData
{
    public class ViewModelProps
    {

        #region DesignTimeViewModelType
        /// <summary>
        /// DesignTime : The type of the ViewModel 
        /// to use (for design time)
        /// </summary>
        public static readonly DependencyProperty 
            DesignTimeViewModelTypeProperty =
            DependencyProperty.RegisterAttached(
            "DesignTimeViewModelType",
                typeof(Type), typeof(ViewModelProps),
                    new FrameworkPropertyMetadata((Type)null));

        /// <summary>
        /// Gets the DesignTimeViewModelType property.  
        /// </summary>
        public static Type GetDesignTimeViewModelType(DependencyObject d)
        {
            return (Type)d.GetValue(DesignTimeViewModelTypeProperty);
        }

        /// <summary>
        /// Sets the DesignTimeViewModelType property.  
        /// </summary>
        public static void SetDesignTimeViewModelType(DependencyObject d, 
            Type value)
        {
            d.SetValue(DesignTimeViewModelTypeProperty, value);
        }
        #endregion

        #region ViewModelType

        /// <summary>
        /// ViewModelType : The type of the ViewModel 
        /// to use (for non design time)
        /// </summary>
        public static readonly DependencyProperty ViewModelTypeProperty =
            DependencyProperty.RegisterAttached("ViewModelType", 
                typeof(Type), typeof(ViewModelProps),
                    new FrameworkPropertyMetadata((Type)null,
                        new PropertyChangedCallback(
                            OnViewModelTypeChanged)));

        /// <summary>
        /// Gets the ViewModelType property.  
        /// </summary>
        public static Type GetViewModelType(DependencyObject d)
        {
            return (Type)d.GetValue(ViewModelTypeProperty);
        }

        /// <summary>
        /// Sets the ViewModelType property.  This dependency property 
        /// indicates ....
        /// </summary>
        public static void SetViewModelType(DependencyObject d, 
            Type value)
        {
            d.SetValue(ViewModelTypeProperty, value);
        }

        /// <summary>
        /// Handles changes to the ViewModelType property.
        /// </summary>
        private static void OnViewModelTypeChanged(
            DependencyObject d, 
            DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)d;
            object vm=null;
            if (Designer.IsDesignMode)
            {
                vm = Activator.CreateInstance(
                    GetDesignTimeViewModelType(d));
            }
            else
            {
                vm = Activator.CreateInstance(
                    GetViewModelType(d));
            }

            if (vm == null)
                throw new InvalidOperationException(
                    "You have to specify a type for the ViewModel");
            
            element.DataContext = vm;
        }

        #endregion

    }
}

 

Then I have 2 ViewModels, one is the actual one, and one is the dummy design time data one.

Something like these

Actual

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace ViewFirstDesignTimeData
{
    public class Window1ViewModel : INPCBase
    {
        private IList<Person> people;
        public IList<Person> People
        {
            get
            {
                if (people == null)
                    LoadPeople();
                return people;
            }
        }

        private void LoadPeople()
        {
            people = new ObservableCollection<Person>();

            //simulate fetching these from web server 
            //or something
            for (int i = 0; i < 20; i++)
            {
                people.Add(new Person(
                    String.Format("runtime firstname_{0}", 
                    i.ToString()),
                    String.Format("runtime lastname_{0}", 
                    i.ToString()),
                    i));
            }
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Design Time

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace ViewFirstDesignTimeData
{
    public class DesignWindow1ViewModel : INPCBase
    {
        private IList<Person> people;
        public IList<Person> People
        {
            get
            {
                if (people == null)
                    LoadPeople();
                return people;
            }
        }

        private void LoadPeople()
        {
            people = new ObservableCollection<Person>();
            people.Add(new Person("Sacha","Barber", 37));
            people.Add(new Person("Marlon","Grech", 30));
            people.Add(new Person("Josh","Smith", 36));
            people.Add(new Person("Pete","O'Hanlon", 43));
            people.Add(new Person("Karl","Shifflett", 53));
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Then all I have to do is mark up my View in the XAML as follows:

NOTE : In this example I also show off a ListBox with an example DataTemplate for Person objects.

<Window x:Class="ViewFirstDesignTimeData.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ViewFirstDesignTimeData;assembly="
    Title="Window1" Height="300" Width="300"
    local:ViewModelProps.ViewModelType=
        "{x:Type local:Window1ViewModel}"    
    local:ViewModelProps.DesignTimeViewModelType=
        "{x:Type local:DesignWindow1ViewModel}" >
    
    <ListBox ItemsSource="{Binding People}">
        <ListBox.ItemTemplate>
            <DataTemplate DataType="{x:Type local:Person}">
                <StackPanel Orientation="Vertical" 
                            TextElement.FontFamily="Arial"
                            HorizontalAlignment="Stretch">
                    <Label FontSize="24" Foreground="Blue"  
                           Content="{Binding FirstName}"/>
                    <Label FontSize="20" Foreground="Black"  
                           Content="{Binding LastName}"/>
                    <StackPanel Orientation="Horizontal">
                        <Label FontSize="14" Foreground="Black"  
                           Content="Age: "/>
                        <Label FontSize="14" Foreground="Black"  
                           Content="{Binding Age}"/>
                    </StackPanel>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
        
    </ListBox>
</Window>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Want proof this works have a look at these screen shots from Expression Blend

Here it the solution in Blend

DesignData1[1]

Extreme close up:

DesignData2[1]

And here is another one, showing the designer surface (right hand side of image) next to the runtime app (left hand side of image):

DesignData3[2]

As always here is a small demo project: demo project.zip

Advertisements
C#, CodeProject

More Weak Help

Every once in a while something happens in conversation, and someone says something that may prove useful.

The other day was one of those days, a bunch of us WPF Disciples were having a chat with Glenn Block (Patterns & Practices / MEF), about some mad arse scheme he had for getting rid of INotifyPropertyChanged smell.

Anyway the result was that Glenn told us about a rather useful class in the .NET 4.0 base class library that made it easy to work with a collection of weak reference objects.

Now I don’t know how many of you are familiar with WeakReference and or WeakEvents, or even the idea of Weak anything. It’s kind of a hot topic these days to allow object references to be maintained without a strong reference to an object, thus allowing objects to be garbage collected. Which is kind of a nice thing.

You may have come across this in PRISMs (CAL) EventAggregator, or Josh/Marlons Mediator messager, which I am using in my own WPF framework Cinch

Anyway the long and short of it is sometimes it is advantageous to use WeakReferences instead of strong ones.

Now most of us have hand crafted our own solutions around WeakReference, but after Glenns little chat the other day he mentioned a new .NET 4.0 class called ConditionalWeakTable<TKey,TValue>

Here is what MSDN says about this handy little class:

Although the ConditionalWeakTable(TKey, TValue) class holds a collection of key/value pairs, it is best thought of as a table rather than a dictionary object. TheConditionalWeakTable(TKey, TValue) class differs from a dictionary in several ways:

  • It does not persist keys. That is, a key is not kept alive only because it is a member of the collection.

  • It does not include all the methods (such as GetEnumerator or Contains) that a dictionary typically has.

  • It does not implement the IDictionary(TKey, TValue) interface.

The ConditionalWeakTable(TKey, TValue) class differs from other collection objects in its management of the object lifetime of keys stored in the collection. Ordinarily, when an object is stored in a collection, its lifetime lasts until it is removed (and there are no additional references to the object) or until the collection object itself is destroyed. However, in the ConditionalWeakTable(TKey, TValue) class, adding a key/value pair to the table does not ensure that the key will persist, even if it can be reached directly from a value stored in the table (for example, if the table contains one key, A, with a value V1, and a second key, B, with a value P2 that contains a reference to A). Instead,ConditionalWeakTable(TKey, TValue) automatically removes the key/value entry as soon as no other references to a key exist outside the table.

That’s quite cool, so we now have a weak dictionary effectively. Here is small example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using System.Threading;

namespace ConsoleApplication2
{
    //Dummy class
    public class HighSchoolClass
    {
        public String Name { get; private set; }

        public HighSchoolClass(String name)
        {
            this.Name = name;
        }
    }

    //Shows how to use the ConditionalWeakTable<TKey,TValue>
    class Program
    {
        static void Main(string[] args)
        {
            //create a ConditionalWeakTable
            ConditionalWeakTable<HighSchoolClass, 
                    List<Int32>> classScores =
                new ConditionalWeakTable<
                    HighSchoolClass, List<Int32>>();

            //initialise some objects to 
            //store in the ConditionalWeakTable
            HighSchoolClass h1 = 
                new HighSchoolClass("Class1");
            HighSchoolClass h2 = 
                new HighSchoolClass("Class2");

            classScores.Add(h1, 
                new List<Int32> { 10, 20, 30 });
            classScores.Add(h2, 
                new List<Int32> { 11, 21, 31 });




            List<Int32> h1List;

            //see if classScores contains h1 : It should
            if (classScores.TryGetValue(h1, out h1List))
                Console.WriteLine(
                    "classScores contains h1 scores");

            PrintList(h1List);

            //set original h1 to null
            h1 = null;

            //collect it
            GC.Collect();

            // Reinstantiate h1 to new instance
            h1 = new HighSchoolClass("Class3");

            //see if classScores contains h1 : It shouldn't
            if (classScores.TryGetValue(h1, out h1List))
                Console.WriteLine(
                    "classScores contains h1 scores");
            else
                Console.WriteLine(
                    "classScores no longer contains h1 scores");

            PrintList(h1List);

            //Add h1 again using new scores
            classScores.Add(h1, 
                new List<Int32> { 100, 200, 300 });

            //see if classScores contains h1 : It should
            if (classScores.TryGetValue(h1, out h1List))
                Console.WriteLine(
                    "classScores contains h1 scores");
            else
                Console.WriteLine(
                    "classScores no longer contains h1 scores");

            PrintList(h1List);


            Console.ReadLine();
        }

        private static void PrintList(List<Int32> h1List)
        {
            if (h1List == null)
            {
                Console.WriteLine("List is null");
            }
            else
            {

                //print h1 scores
                foreach (var score in h1List)
                {
                    Console.WriteLine(
                        String.Format(
                            "Score is : {0}", score));
                }
            }
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

And here is what we get as output when we run this little demo project.

WeakTable[2]

That is quite a handy class, don’t believe me. Try and write your own messager and tell me different.

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

C#, WPF

Embedding PicLens In Your Own Apps

I have always been amazed at just how cool the CoolIris PicLens browser addin is. Its really really cool, top drawer in fact. The other day I was wondering if there would be an API or way that I could use this in my own app, after all I could host something in my own HTML page within a WebBrowser control, which itself is hosted inside my own WPF/WinForms app.

As luck would have it there is a JavaScript API which one can use. So I looked into this and came up with a small demo app which shows you how to embed the CoolIris PicLens browser addin  in your own apps.

I wrote a full article about this over at this url :

http://www.codeproject.com/KB/WPF/emdedPL.aspx

And here is a small screen shot of the demo application that I built running (this is WPF, but the article could easily be applied to WinForms).

finished[1]

In the article I talk through the following things

  • Flash interaction
  • FlashVars
  • SwiftObject creation
  • RSS Media
  • Browser Manipulation (shows how to call JavaScrtipt from WPF C# code)
  • the msHtml namespace and it’s capabilities
  • Mark Of The Web

 

It is a very small article, but I think it is quite a compelling one, just because PicLens is so awesome.

Enjoy

MVVM, WPF

WPF : Re-usable MVVM Ready Breadcrumb control

I have been quite for a little while, but I have been working silently on a new article, which is something that I quite like. Basically I have created a rather unique WPF Breadcrumb system, that supports live preview of the crumbs visited, and is very easy to use in your own projects, and it also supports multiple transitions when changing between crumbs.

Here is a small video showcasing the articles code.


video

And here is a small screen shot of it running
demo

Shameless Plug….Read This

Some of you may not know but I have just under 2 weeks now until I have my 1st child (boy) so this article will be my last one for a little while, so if you have read some of my previous posts or got enjoyment from this blog or learned something from past articles / blog posts, it would be really ace if you could spare the time to vote for my article.

I am dead proud of this article, and I have to say it would be rather nice to go into a restful state (at least for a month or so), going out on a high. So if you feel inclined to vote for my new article, that would be nice.

Here is the link to the article :

http://www.codeproject.com/KB/WPF/WPFBread.aspx

Thanks all..Enjoy

C#, CodeProject, WPF

WPF : A Fun Little Boids Type Thing

I have had a couple of people that have asked me how to draw fast running simulations / games in WPF. The thing that most people try and do is use controls, and move them about using RotateTransforms and TranslateTransforms. This does sound good in theory, but I just don’t think it’s fast enough.

You know if you are writing a game or some sort of physics thing you need speed, and the best way to do that is to do it OnRender.

Luckily most controls in WPF do expose an overridable OnRender method that gives you access to the DrawingContext, see MSDN :

http://msdn.microsoft.com/en-us/library/system.windows.media.drawingcontext.aspx

This is a very cool object that allows you to do all sorts of things. Most people I know that have done any sort of Windows development such as WinForms, would be aware of a OnPaint event, or know how to override Paint. In WPF this is the OnRender() method which has a signature of the following

protected override void OnRender(DrawingContext dc)

Using this override we are easily able to perform quick running graphics operations.

To demonstrate this I have create a small boids type flocking panel, where the user may choose fish or butterfly icons. The fish/butterfly will flock together and tend to hover around the centre of the containing panel, but will be scared shitless of the mouse and shall do everything they can to avoid it.

In order to do this we will need to know how to use the DrawingContext  to do quick operations such as Rotates/Translates.

Lets start with a flocking item shall we. The code for that is as follows:

FlockItem

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Media;


namespace FlockingAvoidance
{
    
    /// <summary>
    /// Flocking Item
    /// </summary>
    public class FlockItem
    {
        public Double X { get; set; }
        public Double Y { get; set; }
        public Double VX { get; set; }
        public Double VY { get; set; }

        public static readonly Int32 ITEM_WIDTH = 30;
        public static readonly Int32 ITEM_HEIGHT = 30;


        /// <summary>
        /// Ctor
        /// </summary>
        public FlockItem()
        {
            this.X = FlockingAvoidanceCanvas.rand.NextDouble() 
                * FlockingAvoidanceCanvas.CANVAS_WIDTH;
            this.Y = FlockingAvoidanceCanvas.rand.NextDouble() 
                * FlockingAvoidanceCanvas.CANVAS_HEIGHT;
            this.VX = 0;
            this.VY = 0;
            this.Move();
        }

        /// <summary>
        /// Centre of item
        /// </summary>
        public Point CentrePoint
        {
            get 
            { 
                return new Point(
                    this.X + (FlockItem.ITEM_WIDTH / 2), 
                    this.Y + (FlockItem.ITEM_HEIGHT / 2)); 
            }
        }

        /// <summary>
        /// Move calculations
        /// </summary>
        public void Move()
        {
            //the speed limit
            if (this.VX > 3) this.VX = 3;
            if (this.VX < -3) this.VX = -3;
            if (this.VY > 3) this.VY = 3;
            if (this.VY < -3) this.VY = -3;


            this.X += this.VX;
            this.Y += this.VY;
            this.VX *= 0.9;
            this.VY *= 0.9;
            this.VX += (FlockingAvoidanceCanvas.
                rand.NextDouble()
                            - 0.5) * 0.4;
            this.VY += (FlockingAvoidanceCanvas.
                rand.NextDouble()
                            - 0.5) * 0.4;

            //go towards center
            this.X = (this.X * 500 + 
                    FlockingAvoidanceCanvas.
                    CANVAS_WIDTH / 2) / 501;
            this.Y = (this.Y * 500 + 
                    FlockingAvoidanceCanvas.
                    CANVAS_HEIGHT / 2) / 501;
        }

    
        /// <summary>
        /// Work out an angle
        /// </summary>
        public static Int32 AngleItem(Double VX, Double VY)
        {
           return (Int32)(FlockingAvoidanceCanvas.
               rand.NextDouble() * 30);
        }


    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Its a very simple data class that holds some positional information. Now we need something that is going to manipulate these FlockItem objects. I have written a small class called FlockingAvoidanceCanvas, which is a custom Canvas control. The code for that is as follows:

FlockingAvoidanceCanvas

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Input;

namespace FlockingAvoidance
{
    /// <summary>
    /// Simple flocking container canvas
    /// </summary>
    public class FlockingAvoidanceCanvas : Canvas, IDisposable
    {
        public static readonly Int32 CANVAS_WIDTH = 500;
        public static readonly Int32 CANVAS_HEIGHT = 500;
        public static Random rand = new Random();

        private List<FlockItem> flockItems
            = new List<FlockItem>();
        private DispatcherTimer timer
            = new DispatcherTimer();
        private Point mousePoint
            = new Point();

        private enum AnimalType 
        { 
            fish = 1, 
            butterfly = 2 
        };
        private AnimalType currentAnimalType
            = AnimalType.fish;
        private BitmapImage imgSource;
        private Double offsetX
            = FlockItem.ITEM_WIDTH / 2.0;
        private Double offsetY
            = FlockItem.ITEM_HEIGHT / 2.0;

        
        /// <summary>
        /// Ctor
        /// </summary>
        public FlockingAvoidanceCanvas()
        {
            this.Width = CANVAS_WIDTH;
            this.Height = CANVAS_HEIGHT;

            for (int i = 0; i < 200; i++)
                flockItems.Add(new FlockItem());

            timer.Interval = TimeSpan.FromMilliseconds(10);
            timer.IsEnabled = true;
            timer.Tick += timer_Tick;

            String imagePath= String.Empty;
            switch (currentAnimalType)
            {
                case AnimalType.butterfly:
                    imagePath = @"Imagesbutterfly.png";
                    break;
                case AnimalType.fish:
                    imagePath = @"Imagesfish.png";
                    break;
                default:
                    imagePath = @"Imagesbutterfly.png";
                    break;
            }

            imgSource = new BitmapImage();
            imgSource.BeginInit();
            imgSource.UriSource = 
                new Uri(BaseUriHelper.GetBaseUri(this),
                    imagePath);
            imgSource.EndInit();
            imgSource.Freeze();

        }


        /// <summary>
        /// Update flocking items
        /// </summary>
        private void timer_Tick(object sender, EventArgs e)
        {
            foreach (FlockItem ItemX in flockItems)
            {
                foreach (FlockItem ItemY in flockItems)
                {
                    if (!Object.ReferenceEquals(ItemX, ItemY)) 
                    {
                        Double dx = ItemY.X - ItemX.X;
                        Double dy = ItemY.Y - ItemX.Y;
                        var d = Math.Sqrt(dx * dx + dy * dy);
                        if (d < 40)
                        {
                            ItemX.VX += 20 * (-dx / (d * d));
                            ItemX.VY += 20 * (-dy / (d * d));
                        }
                        else if (d < 100)
                        {
                            ItemX.VX += 0.07 * (dx / d);
                            ItemX.VY += 0.07 * (dy / d);
                        }
                    }
                }


                Double dxMouse = mousePoint.X - ItemX.X;
                Double dyMouse = mousePoint.Y - ItemX.Y;
                Double dSqrt = 
                    Math.Sqrt(dxMouse * dxMouse + 
                        dyMouse * dyMouse);
                if (dSqrt < 100)
                {
                    ItemX.VX += 1 * (-dxMouse / (dSqrt));
                    ItemX.VY += 1 * (-dyMouse / (dSqrt));

                }
                ItemX.Move();
            }

            //redraw all
            this.InvalidateVisual();
        }


        //Asked to ReDraw so draw all
        protected override void OnRender(DrawingContext dc)
        {
            base.OnRender(dc);

            //draw flocking items
            foreach (FlockItem item in flockItems)
            {
                Double angle = 
                    FlockItem.AngleItem(item.VX, item.VY);

                dc.PushTransform(
                    new TranslateTransform(
                    item.CentrePoint.X, 
                    item.CentrePoint.Y));

                dc.PushTransform(
                    new RotateTransform(angle, 
                        offsetX, offsetY));

                dc.DrawImage(imgSource, 
                    new Rect(0, 0, 
                    FlockItem.ITEM_WIDTH, 
                    FlockItem.ITEM_HEIGHT));
                
                dc.Pop(); // pop RotateTransform
                dc.Pop(); // pop TranslateTransform
            }
        }

        /// <summary>
        /// Store Mouse Point to allow flocking 
        /// items to avoid the Mouse
        /// </summary>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            mousePoint = e.GetPosition(this);
        }

        #region IDisposable Members

        /// <summary>
        /// Clean up
        /// </summary>
        public void Dispose()
        {
            timer.Tick -= timer_Tick;
        }

        #endregion
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

 

Note the OnRender() method, we are able to push/pop Transforms such as

  • RotateTransform
  • TranslateTransform

directly onto the DrawingContext, which makes it very very fast.

Here is a small screen shot of it running with some fish avoiding the Mouse(remember the fish are scared shitless of the Mouse)

fishDemo[2]

As always here is a link to a small demo project:

http://dl.dropbox.com/u/2600965/Blogposts/2010/03/FlockingAvoidance.zip