Showing posts with label silverlight. Show all posts
Showing posts with label silverlight. Show all posts

Monday, March 23, 2009

Silverlight Tour in Chicago

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_tour_in_chicago.htm]

For those interested in Silverlight, there is a Silverlight tour that will be offering classes in Chicago. You can find more details at: http://silverlight-tour.com/. I'm told that this is the first training that is teaching Silverlight 3. While this is not a free event, it may be well worth the cost if you're a Silverlight fan.

Hat tip from Shawn Wildermuth.

Sunday, December 28, 2008

Book: Silverlight in Action

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/book_silverlight_in_action.htm]

I've been excited about Silverlight since I first heard about it over a year ago. Because I started looking at it as an alpha technology, there weren't even the books out yet. So, I used the quickstarts, Bill Reiss's game tutorials, Jesse Liberty's tutorials, and other people's blogs. Well, the books finally started coming out, and I got a copy of Chad Campbell and John Stockton's Silverlight in Action.

 

I liked it. While I got a lot of the background from the online Silverlight resources, the book was just thorough, filled in gaps, and had really good chapters on transforms, animation, and resources (they finally just "clicked" for me).

 

It reminded me of the "good old days" when learning a brand new technology, and you just huddled down with a book and a computer, and hour-by-hour you kept learning something new. I think this book does a really good job of introducing developers to Silverlight.

Thursday, August 28, 2008

LCNUG presentation on Silverlight

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/lcnug_presentation_on_silverlight.htm]

Yesterday I had the opportunity to present at the LCNUG about Silverlight. It was our third meeting and we had about a dozen people. Given the deep talent, it was very humbling. With a small group of talented people, the presentation became more of a back-and-forth discussion, which is great.

 

You can download a copy of the PPT presentation here: silverlightppt.zip. (I tried using Cliff Atkinson's ideas from Beyond Bullet Points.)

 

Here was the abstract:

Silverlight is a new Microsoft technology to radically enhance the UI experience. Silverlight provides several huge benefits, like a vector-graphics API, programming in a compiled .Net language (like C# or VB), a rich object model, and finally solving the cross-browser problem. We will provide a general overview of Silverlight, with an emphasis on common gotchas and where to learn more.

This is the second time I've been able to present at a user group - previously I discussed MSBuild as an automation language.

Tuesday, August 5, 2008

The significance of two-way databinding

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/the_significance_of_twoway_databinding.htm]

ASP.Net came with one-way databinding, and seven years ago that was a big deal. Instead of looping through each element in an array and individually adding it to a dropdown, you could now just bind the array to the dropdown with a single line (or two) of code. This obviously simplified things.

this.MyDropdown.DataSource = myEmployeeList;

this.MyDropdown.DataBind();

However, there were still some complications. Because the Asp.Net CodeBehind still needed to reference the UI control (in this case "MyDropdown"), the CodeBehind still had references to the UI design. For example, if the designer wanted to change the control from a standard dropdown to a custom EmployeePicker, you'd need to update the CodeBehind. As a result, there was still a tight coupling between UI design and coding implementation. This is one-way databinding: the control loads it's data, but it has no way to save data when changed. Sure, you could abstract out styles to CSS, but that essentially just lets you set an adjective, we want to change the nouns.

 

Xaml improves this by offering two-way databinding.  Jesse Liberty has a good tutorial. By merely setting certain attributes, you can now also save any UI changes back to the data object. This allows all the data-binding to be done in the Xaml itself - the CodeBehind can be set up to have no knowledge of what UI controls it's data is being sent to, or modified by. Therefore, two-way databinding allows a true separation between code and design.

 

In the CodeBehind, set the UI control's DataContext:

this.LayoutRoot.DataContext = myObject;

Then, in the Xaml, you can specify the two-way binding on every control within that UI container. The "magic" is the "Mode=TwoWay" sub attribute.

This really clicked for me when I had the opportunity to attend a Rocky Lhotka CLSA presentation, where Rocky whipped up a simple UI form to demonstrate some backend code, and then had a talented UI-oriented coworker modify just the Xaml file (no touching Rocky's technical code). The app went from another list-details WinForm to looking like a Hollywood movie.

 

By allowing a true separation between UI design and coding, Xaml will make it easier for developers to specialize. Before, because everything was so tightly coupled, most developers had to learn both the UI technologies (HTML/JS/CSS...) and the backend technologies (C#, SQL...). Now, it's easier for developers to learn a niche in one tier. I think this is part of how the ever-expanding .Net world works - the overall application is better, and requires more knowledge to develop, but .Net is designed in such a way to spread that knowledge across more people, therefore still making it manageable to develop.

 

Sunday, July 13, 2008

Ideas to encourage your boss to invest in Silverlight

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/ideas_to_encourage_your_boss_to_invest_in_silverlight.htm]

 

Silverlight has a lot of benefits, but as a new technology, it also has problems. As a new technology, it is inevitably riskier as many of the kinks haven't been worked out yet. Managers, who want to avoid unnecessary risk, may shy away from such a technology. However, there are ways to encourage a manager to at least consider Silverlight:

  • Show an actual demo of what Silverlight can do (such as on the gallery). Talk is cheap, but seeing Silverlight in action is powerful.

  • Where feasible, consider developing simple internal tools with Silverlight. Managers almost expect devs to always insist on using the latest technology, regardless of it's business value. But if you believe enough in the tech to invest your own time learning it and applying it to a simple business problem that your department faces - that carries a lot of weight.

  • Emphasize the aspects of Silverlight that would benefit your team - perhaps a rich UI with animating charts, or drag and drop, or rich media, or C# on the client, or cross-browser, etc...

  • If all else fails, consider a little fear-mongering: "Our competitors will be using this". If not Silverlight, at least a Silverlight-competitor like flash.

Some managers were hesitant when JS came out ("it's got cross-browser problems", "not all client support it"), when .Net came out ("J2EE is the established enterprise platform"), when Ajax came out ("it will have security holes"), etc... There's understandably going to be some skepticism with Silverlight too, but that's ok. I personally believe that Silverlight can deliver, and therefore instead of trying to encourage managers to adopt it, managers will be recruiting developers who know it.

 

Monday, July 7, 2008

Two limits with Silverlight (Beta 2)

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/two_limits_with_silverlight_beta_2.htm]

Silverlight has several fundamental benefits. However, there's always a flip-side, and it has some shortcomings too. There are at least two major limits that I see:

  1. Silverlight requires a separate plug-in. Although Flash also requires a plug-in, Flash has something like 98% market share, and is essentially as available as JavaScript. For Silverlight though, this separate plug-in will make many business sponsors take a second look. Of course, MS knows this and is actively working on it - they'll use the full dominance of MS sites (hotmail, msn, etc...) to prompt your to download Silverlight, they'll make it an automatic update so system admins can easily install it across the enterprise, they'll include it in future products, they'll convince popular sites to use it, and hence encourage all those extra viewers to download it. This separate plug-in is a limit, but not a show-stopper, especially for private or intranet apps.

  2. Silverlight is still a very young technology. After the JS release, and a 2.0 alpha, beta1, and beta2, it still doesn't even have a combo box! However, I'd expect that the Microsoft eco-system will rush to fill in these gaps via open source and the Silverlight community. Silverlight is young, but I'd expect the Microsoft faithful and developer community will make it grow fast.

As a developer, I realize that Silverlight has its problems, and an uphill climb, but I'm optimistic. I think that soon its strengths will outweigh its weaknesses.

Sunday, June 29, 2008

Silverlight TruckWars 2.0 - Migrated to SL2 Beta 2

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_truckwars_20___migrated_to_sl2__beta_2.htm]

I migrated Silverlight TruckWars to the SL Beta 2. I started working on TruckWars as a way to learn Silverlight back in the Alpha last year. Being a Microsoft product, there have been two more betas (with plenty of breaking changes), but I've finally migrated it.

 

Things of note - Beta 1 started supporting buttons, which obviously simplified things. For example, it helped me remove keyboard input. It also had some subtle changes that affected the gameplay. Also, there still is no dropdown. Before I was using an HTML dropdown to select the levels. Rather than jump through hoops, I just ceded the dropdown part until the next release.

 

Because the code has been migrated from an alpha, to a beta, to another beta, it's becoming pretty screwy. Not the best, most agile code out there, but good for a demo of what cool stuff Silverlight can do. You can play it here.

 

Monday, May 12, 2008

Converting an object from JSON and back in Silverlight

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/converting_an_object_from_json_and_back_in_silverlight.htm]

JSON (JavaScript Object Notation) provides a convenient way to serialize an object, like "Employee" with first and last name, to a string. This can be very useful in Silverlight apps, such as when you need to pass complex objects to and from a web service.

There are APIs in Silverlight that make it relatively easy to roundtrip an object from a JSON string and back. Here are two wrapper methods.

These use the assemblies System.IO and System.ServiceModel.Web (which contains the necessary namespace System.Runtime.Serialization.Json). It also uses the two static utility methods I blogged about to roundtrip from a MemoryStream and back (GetMemoryStreamFromString and GetStringFromMemoryStream).

    public static T ConvertFromJSON(string strJSON)
    {
      System.Runtime.Serialization.Json.DataContractJsonSerializer d =
          new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));

      MemoryStream m = GetMemoryStreamFromString(strJSON);
      T obj = (T)d.ReadObject(m);

      return obj;
    }

    public static string ConvertToJSON(T obj)
    {
      System.Runtime.Serialization.Json.DataContractJsonSerializer d =
          new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));

      MemoryStream m = new MemoryStream();
      d.WriteObject(m, obj);
      string strJSON = GetStringFromMemoryStream(m);

      return strJSON;
    }

I assume that the code is self-explanatory. Perhaps the only note is that it uses Generics to dynamically set the return type. You could also optimize the methods for performance by instantiating the DataContractJsonSerializer once, and making these utility methods, as opposed to static methods that re-create it every time.

Sunday, May 11, 2008

Is Silverlight just another buzzword?

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/is_silverlight_just_another_buzzword.htm]

There are always new buzzwords coming out in software development. One of those buzzwords gaining more traction is "Silverlight", a Microsoft technology to enable rich UI web applications.

I think that Silverlight is great, and is far more than a buzzword. I see it offering several big benefits:

  1. Graphical API -  Silverlight lets you draw and animate vector graphics. For example, you could easily do a graph. ASP.Net had ways to handle this, but they were slow and cumbersome (for example, use GDI+ to create the image on your server, and then load it up).
  2. Programming a compiled language (like C#) on the client - This is just awesome. Whether you've written an entire physics engine that you're running on the client, or just doing complex validation, the ability to code difficult logic in a first class language like C#, as opposed to a brittle scripting language, is invaluable.
  3. Rich object and event model - no more postbacks, easy to add controls, etc... - You could modify the DOM using JS, but it's so much easier to do this with Silverlight. For example, try dynamically adding an entire complex UserControl with JavaScript - it's doable, but hard. It's one line in silverlight.
  4. Cross-browser compliant - Silverlight just works on the main browsers (IE, Firefox, etc...). No more pulling your hair out because you forgot to check some DOM method nuance.
  5. UI rendered through xaml - Silverlight lets you create your "Form" via a markup language, somewhat like Html, but much cleaner. I think this is a cleaner style than creating it via the codeBehind (like WinForms).

How does Silverlight doe this? In one sense, it "cheats" by requiring a small download (kind of like a flash player). So, Silverlight isn't just like Ajax, which plays by the rules of the web but just stretches them by leveraging the XmlHttpRequest object, but rather Silverlight plays a whole new game.

Wednesday, April 9, 2008

Silverlight Xaml error: "Length cannot be less than zero. Parameter name: length"

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_xaml_error_length_cannot_be_less_than_zero_pa.htm]

I was getting a strange Silverlight compile error in my Page.xaml the other day (while migrating stuff from 1.1 Alpha to 2.0 Beta):

Length cannot be less than zero.
Parameter name: length

At first it sounds like I was setting a wrong value - like trying to reference the "-1" position on a string. But, it actually was a constraint in what namespaces Xaml allows.

 

In 1.1 Alpha, this would be ok:

xmlns:Tank.Core="clr-namespace:Tank.Core;assembly=Tank.Core"

Which you could then reference like so:

However, that line kept throwing the error when I tried to compile.

 

It seems like if I remove the period ".", then it works again, like so:

xmlns:Tank1="clr-namespace:Tank.Core;assembly=Tank.Core"

Strange. I'm not fully sure why, maybe some parsing thing with periods "." in beta, but it was good to have a work-around.

Monday, March 17, 2008

Tips for converting from Silverlight 1.1 Alpha to 2.0 Beta

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/tips_for_converting_from_silverlight_11_alpha_to_20_beta.htm]



As I convert some Silverlight 1.1 Alpha apps to the new 2.0 Beta, I'm running across a lot of issues. So far, all have been solvable. Besides the breaking changes documented on MSDN, here are some others. Also, check the Silverlight Bugs forum if something just doesn't appear to be working right.

Silverlight app does not appear to automatically clip an object moving outside the canvas. I.e. if you define a canvas at 100 x 100, and place an image at (200,200), it will still show up. This can be a problem for widgets with moving parts, especially if the location for those parts are based on complex rules (i..e. the physics engine for a game). You can solve this by having your html host page explicitly set the Silverlight apps' boundaries (instead of using percentages):

            <object data="data:application/x-silverlight," type="application/x-silverlight-2-b1" width="300" height="400">

Image source uses strongly typed bitmap instead of a string url source. You can still dynamically load images.

Downloading xml files by default is now asynchronous. This quickstart tutorial explains how to download files. Sure, it is often a good practice to download the file asycnh so that you do not block the main UI thread. But, for small "legacy" apps that you just want to convert quickly, which download small files from the same server as the silverlight app (i.e. no cross domain), where security is not an issue - it's nice to just have a quick synch method to download files. JackBond wrote a good wrapper utility to get around this, re-pasted here for convenience:
//Method from JackBond at: http://silverlight.net/forums/t/11508.aspx
public static string DownloadXmlStringSync(string url)
{
  ScriptObject request;
  request = HtmlPage.Window.CreateInstance("XMLHttpRequest");

  request.Invoke("open", "GET", url, false);
  request.Invoke("send", "");

  return (string)request.GetProperty("responseText");
}
The source for relative paths has changed, especially when referenced from a user control. Before, relative paths were referenced from root directly. Now, they appear to be referenced from ClientBin (I think). One easy work around is to always just convert your relative paths to absolute ones.

UserControls are no longer just Canvases. This is good and bad.
  • The good is that you now have strongly-typed names for all your controls. No more calling this.GetType().Assembly.GetManifestResourceStream, and then doing (root.FindName("TxtMessage") as TextBlock).Text = strText. Rather, you can just say this.TxtMessage.
  • The Bad is that (at least to my understanding) you cannot make a base class that all your UserControls inherit from. Normally (like in ASP.Net or with 1.1 Alpha Canvases), you could make MyBase which derives from Control, and then have all your custom controls inherit MyBase. But now in Silverlight, you cannot do this because the generated partial class from the Xaml hard-codes a reference to UserControl, and that locks you in. Although, I hope I missed something there, or it's changed in the next beta, because using abstract base classes obviously has its benefits.
Practically, I found it easiest to just delete my Canvas files, and then re-create a UC with the same name, and then manually copy the old canvas code back in.

Reminder: Storyboard needs to be started from Page_Load, not the constructor. Thanks to Bill Reiss for pointing this out. While not a breaking change, it's a good reminder. Before, a Silverlight User Control (i.e. based on the Canvas element) had a Page_Load event, so you could start your StoryBoard from that. But now, the UserControls have a constructor. So, you'll need to add the Load event, and then start your story board from that. Else, you'll waste time like I did wondering "why doesn't this storyboard start?"

Clicking a button, and then making that same button invisible in its click event, causes an exception. More documented on the forums. One suggestion is to set the size to 0.

Third Party controls may mess up your references. If you were using 3rd party controls, it may mess up your name spaces and you get a "Cannot find that type" error. For example, I used a 3rd party button control, and then it could no longer find the new Beta 2.0 buttons, so I kept getting a "Cannot find class or namespace Button...." compile error. I needed to manually add this to the csproj file in the <ItemGroup> for the References:
       
    <Reference Include="System.Windows.Controls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

Application_Exit unexpectedly hit by attaching the mouseHandler to a canvas. It's hard for me to describe this one. But, if you're walking through some mouse-handling code where you attach and detach it, or call the GetPosition on a target canvas that's been removed from the Page , it may throw an exception. I know that's ambiguous, but it's something I was seeing.

Friday, March 14, 2008

Dynamically load an image in Silverlight 2.0 Beta

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/dynamically_load_an_image_in_silverlight_20_beta.htm]

You can easily dynamically load images in Silverlight. given a relative url, you can convert that to an absolute url, and then to an ImageSource, and then use that to set an image. Here's a sample method that takes the relative path, the parent canvas that we'll add the image too, and an x-y point:

 

    private void AddImage(string strRelativeImagePath, Canvas myCanvas, Point p)
    {
      //Convert from RelativePath to AbsolutePath to Uri to ImageSource
      string strFullUrl = GetAbsoluteUrl(strRelativeImagePath);
      Uri u = new Uri(strFullUrl, UriKind.Absolute);
      System.Windows.Media.Imaging.BitmapImage b = new System.Windows.Media.Imaging.BitmapImage(u);

      //Create the image here
      Image img = new Image();
      img.Source = b;
      img.SetValue(Canvas.LeftProperty, p.X);
      img.SetValue(Canvas.TopProperty, p.Y);
      //set other properties here...

      //now add to a canvas:
      myCanvas.Children.Add(img);
    }

 

And just call it like so:

 

    AddImage("MyImage.png", this.parentCanvas, new Point(100, 50));

 

Note some changes from the 1.1 Alpha:

  • Image.Source is no longer just a string url, but rather a strongly-typed class. You need to create an ImageSource object first, such as by instantiating a Bitmap image (this allows other image types, like png, not just bmp).

  • The relative-to directory has changed, such that it's easiest to just convert to an absolute url. When you directly add this to the page, it works fine, but if you add it to a userControl from another class library, then it doesn't. Just using absolute urls makes it work (hence the GetAbsoluteUrl method I blogged about yesterday).

Thursday, March 13, 2008

Silverlight 2.0 Convert Relative Url Paths to Absolute

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_20_convert_relative_url_paths_to_absolute.htm]

As you've probably heard, Silverlight 2.0 Beta 1 is out. You can get it from Silverlight.Net. And Bill Reiss is starting another great tutorial series on Silverlight games.

 

I see a lot of breaking changes (as documented on MSDN), and I'll blog more about my adventures upgrading. One change is that it seems like you need Absolute urls in a few more places (like for accessing xml files or images). Because I like to think in terms of relative urls, here's an easy utility to convert relative to absolute. It should handle both file paths (file:///aaa) and web (http://aaa).

 

    public static string GetAbsoluteUrl(string strRelativePath)
    {
      if (string.IsNullOrEmpty(strRelativePath))
        return strRelativePath;

      string strFullUrl;
      if (strRelativePath.StartsWith("http:", StringComparison.OrdinalIgnoreCase)
        || strRelativePath.StartsWith("https:", StringComparison.OrdinalIgnoreCase)
        || strRelativePath.StartsWith("file:", StringComparison.OrdinalIgnoreCase)
        )
      {
        //already absolute
        strFullUrl = strRelativePath;
      }
      else
      {
        //relative, need to convert to absolute
        strFullUrl = System.Windows.Application.Current.Host.Source.AbsoluteUri;
        if (strFullUrl.IndexOf("ClientBin") > 0)
          strFullUrl = strFullUrl.Substring(0, strFullUrl.IndexOf("ClientBin")) + strRelativePath;
      }

      return strFullUrl;
    }

 

More to come soon...

 

Tuesday, December 11, 2007

Silverlight Image Utilities - clipping and shrinking an image to fit

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_image_utilities__clipping_and_shrinking_an_imag.htm]

For my TruckWars game, I needed to get the profile shot for an image. For example, when you select an object, it displays an image of that object in the dashboard. The two problems are:

  1. What if the image is bigger than the dashboard's profile size? (i..e image is 96x96, but the dashboard only allows 48x48).

  2. What if the image has multiple frames for animation (i.e. image displays as 48x48, but has two frames, and therefore actual size is 96x48).

I wanted a way that given any image, it would clip the image to a single frame, and then shrink it to fit within the allowed-profile size. You can do this if you know the actual image size. In this case, the size of the profile image in the dashboard is 48 pixels. Because I make the animation frames be horizontal (i.e. something with two frames is twice as wide), I scale the image based on height. Then I clip to just the first frame.

 

    public static void MakeProfileImage(ref Image image1, Size szActual)
    {

      //Scale back the img to fit to the view size
      const double profileHeight = 48;
      double dblScale = szActual.Height / profileHeight;
      image1.Width = szActual.Width / dblScale;
      image1.Height = profileHeight;

      //Always clip the view size (in case there were multiple frames for animation)
      RectangleGeometry r = new RectangleGeometry();
      r.Rect = new Rect(0, 0, profileHeight, profileHeight);
      image1.Clip = r;
    }

 

I added this to a ImageUtilities class for reuse later.

 

Monday, December 10, 2007

Silverlight and Globalization: System.FormatException from NumberFormatInfo

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_and_globalization_systemformatexception_from_n.htm]

Through recent error-logging that I added to TruckWars, I found out the sometimes this line would fail:

 double x = Convert.ToDouble("12.5");

 

The input string was static (not user input) as it came from an xml config file. The line threw this exception:

System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(String str, NumberStyles options,
    NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   at System.Number.ParseDouble(String value, NumberStyles options, NumberFormatInfo numfmt)
   at System.Double.Parse(String s, NumberStyles style, NumberFormatInfo info)
   at System.Convert.ToDouble(String value)

At first I thought "of course '12.5' is a valid double." And of course it worked on my machine, and all the servers I was checking. But, the logs still showed this occasional error. Then I thought - what if it's a globalization problem? In other words, for the "en-US" culture, "12.5" is a valid number, but not for other cultures. For example, this would fail for someone in France, where "12,5" is used for a decimal place (note the comma instead of the period).

 

So, I set up a unit test to check for a different culture:

[TestMethod]
public void ParseFromString_Global_Decimal()    
{      
  System.Globalization.CultureInfo culture =
  System.Globalization.CultureInfo.CreateSpecificCulture("fr-FR");
  System.Threading.Thread.CurrentThread.CurrentCulture = culture;

  //run my parsing method here

  //ensure that culture wasn't overridden:
  Assert.AreEqual(culture.Name,
   System.Threading.Thread.CurrentThread.CurrentCulture.Name);
}

And the test failed with the exact error that I expected. I could then fix it by passing in a specific culture (like "en-US") NumberFormat:

private static System.Globalization.NumberFormatInfo _formatNumber =
System.Globalization.CultureInfo.CreateSpecificCulture("en-US").NumberFormat;

//essentially fixed by this:
double x = Double.Parse("12.5", _formatNumber);

I don't normally get this error because for our ASP.Net apps I use a set of utilities that already handled this, and I didn't need to worry about international users for the  windows forms because I only make those for internal development tools. However, it's another reminder why it's nice to have logging for even simple apps.

Wednesday, December 5, 2007

Deploying Silverlight Apps

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/deploying_silverlight_apps.htm]

Silverlight is awesome to deploy. As this forum thread describes, you basically just need your web server to setup the right mime type ("Extension should be .xaml and the Content Type should be application/xaml+xml". I also had ".dll --> application/x-msdownload" because some other blogs recommended it).

 

You can then just ftp the files from your silverlight project (ClientBin, files in the root folder, and any other directories), to your server.

 

After dealing with Winform, ASP.Net, and XNA deployment problems, I was floored that it just actually worked.

 

Because Silverlight runs on the client machine, it is the client's responsibility to have the right stuff installed. However, silverlight makes that really easy by rendering as a single-click download link if the client doesn't have it installed yet.

 

Also note that Silverlight is not a server technology like SQL2005 or ASP.Net. You don't even need an ASP.Net server to run Silverlight. (This is especially practical if you're a hobbyist on a budget, and your host company charges extra to asp-enable your site.).

Monday, December 3, 2007

Silverlight 1.1 global page error handler

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/silverlight_11_global_page_error_handler.htm]

When writing Silverlight TruckWars, I was trying to figure out how to create a global error handler for a page. Eventually I saw the WebApplication.Current.ApplicationUnhandledException event, which I could hook up like so:

 

    public void Page_Loaded(object o, EventArgs e)
    {
        //....
        WebApplication.Current.ApplicationUnhandledException +=
            new EventHandler
            (Current_ApplicationUnhandledException);
    }

 

    void Current_ApplicationUnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
    {
      HandleError(e);
    }

 

Note that Silverlight handles errors differently than ASP.Net. In ASP, you get the yellow error page with a detail exception message (by default). However, in Silverlight, it just stops executing your code and returns to the caller, leading to weird behavior.

Monday, November 26, 2007

Creating a transparent, animating, and opaque image in Silverlight 1.1

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/creating_a_transparent_animating_and_opaque_image_in_silve.htm]

Every sprite-based game eventually needs two things from images: transparency and animation. In TruckWars, I use this all over the place.

 

Transparent - You can make an image be transparent by just using a transparent png (I don't think Silverlight supports transparent gifs yet), which you can easily create in Paint.Net.

Opaque - You can also make it opaque by just setting the built-in Silverlight opacity property on an image.

 

Animation is a litter harder. There are different takes on this. Two main categories of approaches are (A) have a single large image which contains all the frames, and then clip that image every x milliseconds, or (B) have a separate image for each animation frame, and then update the image source every x milliseconds. I prefer option A because it's much easier to manage, but requires a little extra coding to do the clipping.

 

About option A, for example, the smoke cloud consists of four separate frames:

 

Smoke

 

You then animate it by displaying one frame every x milliseconds. (Note that I used sprites for a smoke effect instead of a particle system because the sprite is much faster). The question becomes now - how do I clip this image? Ultimately I'll use the Silverlight clipping property - but how do you set what the clip should be?

 

Some people would just use the built-in storyboard technique with key-frames to update the clip region. My problem with that is (1) I didn't know how to manage that for multiple objects such that I could pause-on-demand and have the animation freeze, and (2) that's Silverlight-specific, so learning it wouldn't help me in other scenarios, like XNA, (3) I just didn't have the motivation to learn the new technique yet. So, instead I decided to build code to manage my own clip updating.

 

You can see the code for this in the TruckWars download: Tank\Tank.Test\Tank.Core\SpriteBase.cs, however, here's a quick overview. The code runs on the same gameClock than runs everything else. It keeps track of the current frame, and has two animation options: Repeat or RunOnce. For example, the water repeats its animation, but the smoke only runs once and then gets removed from the game. At every cycle of the gameloop, it checks if enough time has passed (in this case, 200 milliseconds), and then potentially draws the next frame.

 

To actually display the new animation requires two things: (1) clip the image to the size of a single frame (48 x 48), but then also offset the images left position such that that frame appears in the same place

 

      //set clip
      RectangleGeometry r = new RectangleGeometry();
      r.Rect = new Rect(intFrameIndex * this.Size.Width, 0, this.Size.Width, this.Size.Height);

      MainImage.Clip = r;

      //apply offset
      this.MainImage.SetValue<double>(Canvas.LeftProperty, 0 - intFrameIndex * this.Size.Width);

 

 

Here's more complete code:


    #region Animation

    protected int _intFrameIndex = -1;  //Default to -1 because Update runs first, and increments it to 0 right away
    private const int AnimationFrameCount = 4;

    private int _TimeToWaitForAnimation = 200;
    public int TimeToWaitForAnimation
    {
      get
      {
        return _TimeToWaitForAnimation; //wait x milliseconds between animation frames
      }
      set
      {
        _TimeToWaitForAnimation = value;
      }
    }

    protected double _dblTimeOfLastAnimation = 0;


    public bool FinishedAnimationCycle
    {
      get
      {
        return ((_intFrameIndex) >= AnimationFrameCount);
      }
    }


    private AnimationCycle _AnimationCycle = AnimationCycle.Repeat;
    public AnimationCycle AnimationCycle
    {
      get
      {
        return _AnimationCycle;
      }
      set
      {
        _AnimationCycle = value;
      }
    }

    protected void ResetAnimation()
    {
      _dblTimeOfLastAnimation = 0;
      _intFrameIndex = -1;
    }

    public void DrawAnimation(double dblElapsedGameTimeMS)
    {
      bool blnShouldUpdate = ShouldUpdate(dblElapsedGameTimeMS);
      if (!blnShouldUpdate)
        return;

      //-----------
      _dblTimeOfLastAnimation = dblElapsedGameTimeMS;

      _intFrameIndex++;

      if (this.AnimationCycle == AnimationCycle.Repeat)
      {
        if (_intFrameIndex + 1 > AnimationFrameCount)
          _intFrameIndex = 0;
      }
      //-------------

      DrawFrame(_intFrameIndex);
    }

    private bool ShouldUpdate(double dblElapsedGameTimeMS)
    {
      if (Convert.ToInt32(dblElapsedGameTimeMS - _dblTimeOfLastAnimation) < TimeToWaitForAnimation)
        return false;
      else
        return true;
    }

    ///


    /// Animation is done by giving a single image with multiple frames, and then displaying one frame at a time.
    /// For performance reasons, this only redraws the image if the FrameIndex has changed. Therefore repeatedly calling this
    /// method in a loop should not degrade performance, as it will just cancel out once the image is set (and until the image is changed).
    ///

    ///
    public void DrawFrame(int intFrameIndex)
    {
      if (intFrameIndex == _intLastDrawnFrame)
        return; //Already at the correct frame, don't waste resources re-drawing.

      //set clip
      RectangleGeometry r = new RectangleGeometry();
      r.Rect = new Rect(intFrameIndex * this.Size.Width, 0, this.Size.Width, this.Size.Height);

      MainImage.Clip = r;

      //apply offset
      this.MainImage.SetValue<double>(Canvas.LeftProperty, 0 - intFrameIndex * this.Size.Width);

      _intLastDrawnFrame = intFrameIndex;

    }

    private int _intLastDrawnFrame = -1;

    private Image MainImage
    {
      get
      {
        return (this.root.FindName("Image1") as Image);
      }
    }

    #endregion

 

 

Monday, November 19, 2007

Benefits of Farseer 2D Physics Engine

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/benefits_of_farseer_2d_physics_engine.htm]

I recently released Silverlight TruckWars v1.3, whose big enhancement was to incorporate the Farseer 2D Physics Engine. There are several benefits I like about Farseer:

  1. It just works - The collision algorithms just work. Before I had some code to do polygon detection. It was good code that I found online. And my use of it was okay when comparing two simple bodies, but it broke down with bigger things - like 5 units all pushing on each other. Because TruckWars is intensive on objects interacting with each other, this was important.

  2. Automatic pushing of objects - Before I had special code for the crate object (that you can push), but now, pushing objects is automatically handled.

  3. Collision categories - Not every object collides with every other object. For example, a fireball hovers over water, but a tank is blocked by it.

  4. Static objects - Farseer recognizes that sometimes, something is just impenetrable and unmovable - like the walls of a game board. Whereas in the real world, enough force will eventually push through something, I didn't want to allow that in the game world.

  5. Circular geometries - Farseer implements circular geometries via polygons, and lets you specify the number of sides for precision. My previous collision code only handled rectangles.

  6. Collision callback - You can add an extra method that fires on any collision. That method can then return true (to continue normal collision behavior) or false (to ignore collisions for that specific case).

  7. Body and Geometry have object tags, so I can associate them with a creature.

  8. API - The API is just clean, especially if you have any physics background. It both maps to standard physics knowledge, as well as provides desired method calls that you'd want to actually program something.

It took some extra effort, but it was well worth it. The engine feels much more solid now.

 

It's also worth noting, that Farseer almost makes you to do it correctly - by applying forces instead of directly setting positions. That's how the real phsycial world works. This also makes you appreciate what's involved in seemingly simple things - moving at a constant rate, having zero-turning radius, stopping a unit on demand (perfect breaks). You can achieve each of these by setting the correct properties. While it's tempting to just set the positions because then you can control everything, that becomes a nightmare with complex collisions.

 

I see a similar parallel to Enterprise development. It's tempting to just whip out some hack, but then as you scale up, that hack always comes back to haunt you.

 

I've open-sourced TruckWars on CodePlex - so you can see all the places Farseer is used. One place is from the MoveTowardTarget
 method in Tank\Tank.Test\Tank.Core\MovementStrategies.cs:

//Always clear angular velocity
c.Body.AngularVelocity = 0f;

//update direction and position
double dist = MovementUtilities.GetDistance(c.Position, c.TargetPosition);
if (dist > c.Size.Width / 2)
{
  ReleaseBreaks(c);

  Vector2 force = new Vector2();
  force.X = (float)(c.TargetPosition.X - c.Position.X);
  force.Y = (float)((c.TargetPosition.Y - c.Position.Y));
  float forceAmount = c.Thrust * PhysicsHelper.MovementForce;
  force = force.Normalize() * forceAmount;

  //Prevent sheering --> make LinearVelocity always be in direction of force
  c.Body.LinearVelocity = force.Normalize() * c.Body.LinearVelocity.Length();
  c.Body.ApplyForce(force);


  c.UpdateDirection();
}
else
{
  //You've reached your target, so apply the breaks
  ApplyBreaks(c);
}

This clears the angular velocity to prevent spinning, normalizes the force to push you in  a straight direction, and incorporates applying & releasing breaks to allow for quick stopping. Here are the two break methods:

public static void ApplyBreaks(CreatureBase c)
{
  //Need to "release breaks" when moving again
  c.Body.ClearForce();
  c.Body.LinearDragCoefficient = 4 * PhysicsHelper.LinearDragBreaks;
  c.Body.AngularVelocity = 0f;
  c.Body.ClearTorque();
}

private static void ReleaseBreaks(CreatureBase c)
{
  c.Body.LinearDragCoefficient = (float)(c.LinearDrag * PhysicsHelper.LinearDragMovement);
}

Sunday, November 18, 2007

Releases Silverlight TruckWars v1.3 - OpenSourced on CodePlex

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/releases_silverlight_truckwars_v13__opensourced_on_codeple.htm]

Silverlight TruckWars is a real-time strategy game built entirely in Silverlight. I recently releases version 1.3. The biggest thing is that I've now open-sourced it on CodePlex.

 

Check it out:

I've also added several enhancements

  • Added hold-able objects, like a key

  • Gameplay enhancmeents.

    • The biggest one is that selecting any creature now provides help text.

    • Destroying an enemy unit now can optionally provide a "treasure", like a powerup or key.

  • Lots more powerups:

    • Bounce - projectile bounces off walls

    • Thrust - doubles your thrust

    • Clone - clones the creature that got the powerup

    • Health - plus 50 health points

  • New creatures & levels

    • LandMine

    • Giant Tank