 |
|
 |
|
|
Adding Behaviors Programmatically |
|
Andy's Blog
|
By Andy Beaulieu on
8/23/2009 7:31 AM
|
|
|
Expression Blend 3 has great design-time support for Behaviors – you can simply drag Behaviors from the Asset Library onto the Artboard (or Objects + Timeline Window), and the Behaviors are applied to an element. But what if you want to add Behaviors through code at runtime? We can do this by using the BehaviorCollection and GetBehaviors method in the System.Windows.Interactivity Namespace…
using
System.Windows.Interactivity;
{…}
BehaviorCollection behaviorCollection = Interaction.GetBehaviors(box);
behaviorCollection.Add(new MouseDragElementBehavior());
Adding Physics Behaviors Programmatically
In a previous blog post, I had a question about adding Behaviors at runtime for the Physics Helper Behaviors. These behaviors are a little different in that they are dependent on a PhysicsControllerBehavior as kind of a “master controller.” There are also dependencies for some of the other Behaviors – for example a PhysicsJointBehavior depends on the existence of a PhysicsObjectBehavior for the bodies it is joining. So there is an additional call necessary for some of the Physics Behaviors (this may be fixed in a future release).
Here is a code example. Suppose you have a Rectangle named “ground” that you want to make a Static Physics Object (one that stays in place, like some ground), and an Ellipse named “ball” that you want to make a physics object.
PhysicsControllerMain _physicsController
= LayoutRoot.GetValue(PhysicsControllerMain.PhysicsControllerProperty)
as PhysicsControllerMain;
// add physics behavior
to an ellipse named "ball"
behaviorCollection = Interaction.GetBehaviors(ball);
behaviorCollection.Add(new PhysicsObjectBehavior());
_physicsController.AddPhysicsBody(ball.GetValue(PhysicsObjectMain.PhysicsObjectProperty) as PhysicsObjectMain);
// add static
body behavior to a rectangle named ground
PhysicsObjectBehavior behPhysObj
= new PhysicsObjectBehavior();
behPhysObj.IsStatic = true;
behaviorCollection = Interaction.GetBehaviors(ground);
behaviorCollection.Add(behPhysObj);
_physicsController.AddPhysicsBody(ground.GetValue(PhysicsObjectMain.PhysicsObjectProperty) as PhysicsObjectMain);
The key is the additional AddPhysicsBody call, which informs
the PhysicsController that a new object should be added to the simulation.
Adding User Controls Programmatically
Most of the time, you will want to define Joints and other Physics Behaviors inside separate User Controls. This will help keep your assets in manageable parts. You can still add these user controls dynamically through code, and there is an example of this in the Physics Helper download (DemoBehaviors2, which dynamically adds a RagDoll):
// add a user control,
then apply physics
ucRagDoll ragdoll = new ucRagDoll();
LayoutRoot.Children.Add(ragdoll);
_physicsController.AddPhysicsBodyForCanvasWithBehaviors(ragdoll.LayoutRoot);
Keep in mind that the Physics Helper caches any Boundaries that it detects (that is, the outline of your Physics Elements). This is because it is an expensive operation to trace the outline of each element. So you should try to have one instance of your Physics Objects on screen at startup so that subsequent instances are added quickly.
More Info on Physics Helper
Physics Helper on Codeplex
Farseer Physics on Codeplex
Physics Helper 3: Now with WPF Support
Coming Soon:Fluid Container Behavior
|
 |
|
Comments (6)
|
|
|
|
Coming Soon: Fluid Container Behavior! |
|
Andy's Blog
|
By Andy Beaulieu on
8/20/2009 2:22 PM
|
|
|
Have you figured out that Behaviors are the greatest thing since sliced bread? You WILL... only a matter of time :) I'm still having fun in my spare time adding to the Physics Helper Behaviors, and coming soon to the library is a Fluid Container Behavior, which can be used to simulate water and waves.
I threw this water/ragdoll test together using the new behavior (oh, and no code!):
MUCH of the credit for the Fluid Container goes to Jeff Weber - besides being the creator of the Farseer Physics Engine, he also published an inspiring Water Physics Demo. And he was nice enough to publish the source code as well! So it took about 60 minutes to wrap Jeff's sample code into a Behavior. THANK YOU JEFF!
|
 |
|
Comments (7)
|
|
|
|
|
Physics Behaviors for Silverlight 3! |
|
Andy's Blog
|
By Andy Beaulieu on
7/15/2009 7:21 PM
|
|
|
|
Today I released an initial version of “Physics Helper 3” to Codeplex. This includes a collection of new Behaviors for Expression Blend which makes it very quick to create physics games and simulations in Silverlight using the Farseer Physics Engine. This release requires Silverlight 3 and Expression Blend 3, which you can get here.
DOWNLOAD PHYSICS HELPER 3
Demos
There are four new demos using Behaviors, plus the original demos depending on the older User Control model. Note that version 3 still supports the User Control model, but I would recommend checking out the new Behaviors model as there is nice design time support in Blend.
VIEW ALL DEMOS
|
| Falling Astronaut | |
|
| Rag Doll | |
|
| Truck w/Camera | |
|
| Flying Astronaut | |
Getting Started Videos
I have a couple of quick (10 min) screen casts which show how to use the new Physics Behaviors:
VIDEO ON "THE BASICS": VIEW | DOWNLOAD
VIDEO ON "JOINTS + PROGRAMMING": VIEW | DOWNLOAD
Enhancing Behaviors with Code
Behaviors are great, but I can't imagine creating a full game using only canned behaviors. So at some point, you will need to add code to do AI, control levels and game state, and other tasks. Here are some pointers to get started with that:
Once you drop a PhysicsController Behavior onto your main Canvas, you can later get a reference to that Physics Controller (the object that contains the simulation context) in code. Suppose your main Canvas is named "LayoutRoot" then you can get a reference as follows:
PhysicsControllerMain _physicsController = LayoutRoot.GetValue(PhysicsControllerMain.PhysicsControllerProperty) as PhysicsControllerMain;
After you have a reference to the PhysicsController, you can modify any of the Farseer Physics Geometry or Body objects by getting a reference throught the PhysicsObjects dictionary:
_physicsController.PhysicsObjects["ball"].GeometryObject.RestitutionCoefficient = 1.3F;
... and you can also get a reference to the oringal XAML UI Element using the uiElement property of the Physics Object:
Ellipse ball = _physicsController.PhysicsObjects["ball"].uiElement as Ellipse; ball.Fill = new SolidColorBrush(Colors.Red);
Feedback and Issues
This is an initial release and my first swing at Behaviors, so I'm sure there will be issues and suggestions. Please let them fly on the Codeplex Forum, and I hope you have fun!
|
 |
|
Comments (8)
|
|
|
|
|
Physics Helper 3: Coming Soon! |
|
Andy's Blog
|
By Andy Beaulieu on
7/10/2009 11:08 AM
|
|
|
|
Silverlight 3 and Blend 3 introduce the concept of Behaviors, which make it easy to add common functionality using drag/drop inside Blend.
I’ve been working on creating a set of Behaviors for the Physics Helper Library, and have made good progress. Using these behaviors, you can very quickly put together physics-based games and simulations with little or no code. (Ok, I don’t really believe in the no-code scenario … but you can do some pretty neat stuff without code). Another new feature in the library is the ability to automatically determine the outline (collision boundary) of raster images (such as a PNG).
I’ll be releasing this updated library soon on Codeplex, but here is a screencast I put together to show the Behaviors in action:
[DOWNLOAD THE VIDEO]
Or click below to view it:

|
 |
|
Comments (4)
|
|
|
|
"Printing" in Silverlight 3 with WriteableBitmap |
|
Andy's Blog
|
By Andy Beaulieu on
7/10/2009 8:30 AM
|
|
|
|
One of the high-profile missing features in Silverlight has been Printing support. If you have ever tried to print a web page containing Silverlight content, what you saw on the printed page may be skewed or even missing altogether! So, what if you wanted to print a portion of your Silverlight screen, or take a “snapshot” image of the Silverlight UI to include in a report or other printable format?
Silverlight 3 can accomplish these scenarios using the WriteableBitmap API. WritableBitmap includes a Render method which can snag all of the pixels of a given UI Element and place them into a buffer for manipulation.
In this demo, I’ll show how to take a “snapshot” of a Silverlight UI screen, upload the image to a web server, and include it in a Report Viewer (RDLC) report. You could easily modify these steps to save the snapshot to an image file on the server, or otherwise manipulate the pixels.
VIEW THE DEMO
DOWNLOAD THE SOURCE
The steps used in the demo are as follows:
1. Render the Silverlight UI to a WriteableBitmap, passing in the UI Element and an arbitrary Transform. In the code below, the content of a Canvas named cnvSource is rendered to the WriteableBitmap. We pass in an empty TranslateTransform simply because one is required by the constructor:
WriteableBitmap bitmap = new WriteableBitmap(cnvSource, new TranslateTransform());
2. Convert the WriteableBitmap pixels to a PNG using Joe Stegman's PNG encoder.
EditableImage imageData = new EditableImage(bitmap.PixelWidth, bitmap.PixelHeight);
for (int y = 0; y < bitmap.PixelHeight; ++y)
{
for (int x = 0; x < bitmap.PixelWidth; ++x)
{
int pixel = bitmap.Pixels[bitmap.PixelWidth * y + x];
imageData.SetPixel(x, y,
(byte)((pixel >> 16) & 0xFF),
(byte)((pixel >> 8) & 0xFF),
(byte)(pixel & 0xFF),
(byte)((pixel >> 24) & 0xFF)
);
}
}
Stream pngStream = imageData.GetStream();
| NOTE that this PNG encoder does NOT include compression! This would be a good optimization to add, but also note that the GZipStream class is not present in Silverlight, so you would need to use an outside compression library such as SharpZipLib.
3. At this point, we have the PNG bytes in a stream, and you could take several approaches to get these bytes up to the server – such as using an Http Handler (ASHX). In this demo, we’ll place the bytes into a hidden field on the ASPX page and post the page back to the server for inclusion in a report. To do this, we’ll translate the PNG bytes into a string using Base64 encoding:
byte[] binaryData = new Byte[pngStream.Length];
long bytesRead = pngStream.Read(binaryData, 0, (int)pngStream.Length);
string base64String =
System.Convert.ToBase64String(binaryData,
0,
binaryData.Length);
// save the encoded PNG bytes to the page
HtmlDocument document = HtmlPage.Document;
HtmlElement txtPNGBytes = document.GetElementById("txtPNGBytes");
txtPNGBytes.SetProperty("value", base64String);
// this calls a js function "postBackPrint" which will cause a postback
HtmlPage.Window.CreateInstance("postBackPrint", new string[] { }); |
4. Now that we have our bytes up on the server, we can decode them and feed them to a ReportViewer (RDLC) report. This will give us a nicely printed format and the ability to export to PDF:
string bytes64 = Request["txtPNGBytes"];
byte[] imageBytes = System.Convert.FromBase64String(bytes64);
DSReportPrintImage ds = new DSReportPrintImage();
DataRow drImage = ds.Tables[0].NewRow();
drImage["ImageBytes"] = imageBytes;
ds.Tables[0].Rows.Add(drImage);
ReportViewer1.LocalReport.ReportPath = "ReportPrintSilverlight.rdlc";
ReportDataSource src = new ReportDataSource("DSReportPrintImage_ImageData", ds.Tables[0]);
ReportViewer1.LocalReport.DataSources.Add(src);
ReportViewer1.LocalReport.Refresh(); |
That’s it! I really think this use of WriteableBitmap as a snapshot/print function will be useful in some of my projects that need to capture the current view of the Silverlight application.
|
 |
|
Comments (28)
|
|
|
|
Improved HitTest Method for Silverlight 3 |
|
Andy's Blog
|
By Andy Beaulieu on
7/10/2009 8:24 AM
|
|
|
|
With the new Silverlight 3 WriteableBitmap class, it’s possible to do pixel-perfect collision detection (a “HitTest”) on bitmap (raster) images. In a previous blog post, I showed how we can do a HitTest on Vector graphics in Silverlight 2. In this blog post, we’ll enhance the collision detection to handle BOTH Vector graphics and Raster graphics in Silverlight 3.
VIEW THE DEMO
DOWNLOAD THE SOURCE
Looking at the demo code, one of the main routines is the CheckCollisionPoint class, which does a HitTest on a UI Element at a specific Point. If the element is an Image, then a WriteableBitmap object is used to check the specific pixel point (this WriteableBitmap is created in the CheckCollision method, shown later.). If the element is not an image, then we assume it is Vector based and we use the VisualTreeHelper class for a hit test:
public bool CheckCollisionPoint(Point pt, FrameworkElement control, FrameworkElement controlElem)
{
if (controlElem is Image)
{
// NOTE that we saved the WB in the Tag object for performance.
// in a real app, you would abstract this in your sprite class.
WriteableBitmap wb = controlElem.Tag as WriteableBitmap;
int width = wb.PixelWidth;
int height = wb.PixelHeight;
double offSetX = Convert.ToDouble(control.GetValue(Canvas.LeftProperty));
double offSetY = Convert.ToDouble(control.GetValue(Canvas.TopProperty));
pt.X = pt.X - offSetX;
pt.Y = pt.Y - offSetY;
int offset = Convert.ToInt32((width * pt.Y) + pt.X);
return (wb.Pixels[offset] != 0);
}
else
{
List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(pt, controlElem) as List<UIElement>;
return (hits.Contains(controlElem));
}
} |
The CheckCollisionPoint method is used by the CheckCollision method, which checks for a collision between two UI elements. Note that we always want to do a quick Rect.Intersect test on the bounds of the elements before doing the slower per-pixel Hit Test. This method also creates the WriteableBitmap and stores it in the Tag property (you may want to abstract this into a Sprite class in a real application):
private bool CheckCollision(FrameworkElement control1, FrameworkElement controlElem1, FrameworkElement control2, FrameworkElement controlElem2)
{
// first see if sprite rectangles collide
Rect rect1 = UserControlBounds(control1);
Rect rect2 = UserControlBounds(control2);
rect1.Intersect(rect2);
if (rect1 == Rect.Empty)
{
// no collision - GET OUT!
return false;
}
else
{
bool bCollision = false;
Point ptCheck = new Point();
// NOTE that creating the writeablebitmap is a bit intense
// so we will do this once and store results in Tag property
// in a real game, you might abstract this into a Sprite class.
if (controlElem1 is Image)
controlElem1.Tag = GetWriteableBitmap(control1);
if (controlElem2 is Image)
controlElem2.Tag = GetWriteableBitmap(control2);
// now we do a more accurate pixel hit test
for (int x = Convert.ToInt32(rect1.X); x < Convert.ToInt32(rect1.X + rect1.Width); x++)
{
for (int y = Convert.ToInt32(rect1.Y); y < Convert.ToInt32(rect1.Y + rect1.Height); y++)
{
ptCheck.X = x;
ptCheck.Y = y;
if (CheckCollisionPoint(ptCheck, control1, controlElem1))
if (CheckCollisionPoint(ptCheck, control2, controlElem2))
{
bCollision = true;
break;
}
}
if (bCollision) break;
}
return bCollision;
}
} |
A quick note about performance: This method provides a simple way of getting pixel-perfect collision detection in Silverlight 3. But if you have a lot of elements that are colliding simultaneously (without being destroyed), this may not perform well. Also, high velocity objects could pass through each other.
|
 |
|
Comments (10)
|
|
|
|
|
Blend Artboard Exceptions and Loaded Event |
|
Andy's Blog
|
By Andy Beaulieu on
6/3/2009 8:35 AM
|
|
|
|
It can be frustrating when you are coding along in Silverlight, and you open up a UserControl in Blend only to see something like the following:
 An Exception was thrown. InvalidOperationException: HtmlPage_NotEnabled StackTrace InnerException: None
This is often caused by a UserControl that has a Loaded event with some code that isn't happy inside Blend's artboard. That's right - when you preview a UserControl inside Blend, the Loaded event for the control actually fires, and code in that handler is executed! You may have noticed this when you place a StoryBoard.Begin() call in the Loaded event, Blend will actually show the animation on the artboard.
So how do we workaround these exceptions? First, expand the StackTrace arrow, and you'll get some more details. In this case, we can see that the Loaded event for a UserControl named "ucResults" is the culprit:

Now that we know which Loaded event is causing the issue, take a look at the code and see what is going on. In this case, the code is accessing the HtmlPage class - which would not be available to Blend when it hosts the control on the artboard:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
// Did the QueryString contain an Email address?
if (HtmlPage.Document.QueryString.Keys.Contains("Email") )
txtEmailTo.Text = HtmlPage.Document.QueryString["Email"].ToString();
}
Luckily, there is a special class, System.ComponentModel.DesignerProperties, which allows us to check at runtime whether we are in Design Mode or not. We can just add a check on this, and if we are in Design Mode, we get out of the Loaded event handler:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(this))
return;
// Did the QueryString contain an Email address?
if (HtmlPage.Document.QueryString.Keys.Contains("Email") )
txtEmailTo.Text = HtmlPage.Document.QueryString["Email"].ToString();
}
|
 |
|
Comments (1)
|
|
|
|
|
 |
|
 |
|
|