| Comments

Yesterday there was quite a buzz around something Microsoft just released called “NuPack” which is described as:

NuPack is a free open source package manager that makes it easy for you to find, install, and use .NET libraries in your projects. It works with all .NET project types (including, but not limited to, both ASP.NET Web Forms and ASP.NET MVC).

NuPack enables developers who maintain open source projects (for example, projects like Moq, NHibernate, Ninject, StructureMap, NUnit, Windsor, RhinoMocks, Elmah, etc) to package up their libraries and register them with an online gallery/catalog that is searchable.

It’s a pretty cool mechanism for getting .NET libraries.  For other open source developers this concept isn’t something new (i.e., gems).  But for .NET developers it might be because it is a difference from the way we typically have received dependent and 3rd party assemblies for our projects.  It provides a PowerShell script mechanism for adding packages as well as the well-known “Add Reference” gesture for VS developers.

All the initial information around NuPack has been from folks like Scott Guthrie, Phil Haack, David Ebbo, etc.  You might recognize these names from the ASP.NET world.  In fact if you do your first “list-package” command you’ll see a lot of ASP.NET-related packages.  If you didn’t know any better and weren’t an ASP.NET developer you might ignore this.  However, NuPack is for everyone!

One of the most commonly installed items for Silverlight developers after the toolset is the Silverlight Toolkit.  It is a plethora of controls that frankly you probably can’t live without (at least one of them) if you are developing a broad Silverlight application.  After spending a few minutes reading on NuPack I decided to explore.

My initial playground – Building the MyNuLibrary package

I first just wanted to play around and created a Silverlight class library MyNuLibrary.  It has one class Math that just has two functions.  The contents is pretty much irrelevant here.  I wanted to create a package for this and test it out.

In my project structure I decided to put the tools in the project.  To be clear, this felt completely wrong.  My “tools” shouldn’t be a part of my project.  I think this will be resolved with better build task integration, but for now to create a package you need the NuPack.exe tool.  I put that and NuPack.Core.dll in a Tools folder in my project.  I then created my manifest (MyNuLibrary.nuspec) file as follows and put it in the root of my project (marking the build action as None of course):

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
   3:     <metadata>
   4:         <id>MyNuLibrary</id>
   5:         <version>1.4</version>
   6:         <authors>
   7:             <author>Tim Heuer</author>
   8:         </authors>
   9:         <language>en-US</language>
  10:         <description>Custom Silvelright Math library</description>
  11:     </metadata>
  12:     <files src="bin\debug\MyNuLibrary.dll" target="lib\SL4" />
  13: </package>

You can see that the <files/> node tells the package where to put things.  Notice the lib\SL4 target attribute value.  This tells NuPack (and installers of the package) that this library is really targeting Silverlight 4 (uses TargetFramework value of the source project when installing the package to verify).  There is more information on the NuPack project site about this.

Since my tools were relative to the root I needed to provide the bin\debug path in the source attribute value.  Initially this caused me problems as the NuPack.exe unmodified then bundled them into lib\SL4\bin\debug\MyNuLibrary.dll path.  When installing (using add-package) it failed because it said it couldn’t find any assembly that would match my project.  Apparently right now NuPack expects binaries to be in the framework folders, but not in tree structure.  For me, I modified PathResolver (in NuPack.Core):

   1: if (actualPath.StartsWith(basePath, StringComparison.OrdinalIgnoreCase))
   2:     {
   3:         //packagePath = actualPath.Substring(basePath.Length).TrimStart(Path.DirectorySeparatorChar);
   4:         packagePath = Path.GetFileName(actualPath);
   5:     }
   6:     else
   7:     {
   8:         packagePath = Path.GetFileName(actualPath);
   9: }

for my needs.  I’ve communicated the issue to some folks on the NuPack core team and I think there may be some changes (I haven’t submitted a patch yet until I understand the need for the original code path).  And yes, I realize my change above effectively makes the if…else do the same thing and thus the if…else isn’t needed.  Again, I’m awaiting confirmation of the valid scenarios before submitting what I think the patch should be.

All that aside, I then added a post-build event to my project (note I added quoted params here which is not in the CodePlex documentation sample – if you don’t use quotes and you have spaces in your paths, then your post-build will fail…adding the quotes saves you time):

   1: "$(ProjectDir)Tools\NuPack.exe" "$(ProjectDir)MyNuLibrary.nspec"

And upon build I now have a MyNuLibrary.1.4.nupkg file as an artifact of my build.  Done!  If we look at the contents (it’s actually just a OPC ZIP file we can see the structure and you’ll notice that our binary is in the lib\SL4 folder.

Surfacing MyNuLibrary package

The next step is to have your package visible somewhere.  The NuPack VS shell and the Add Reference dialog can recognize 2 types of paths: an Atom feed or a local directory.  For testing I just used a local directory using the settings in VS:

NuPack source locations

And then when I run list-package it shows only my packages from that “feed”:

NuPack list-package output

Now I can consume it.

Consuming MyNuLibrary package

Now I can start a new Silverlight project and open up the VS Package Window and initiate a command to add the package.  I call add-package MyNuLibrary and see that my Silverlight project gets the reference automatically included in my project (and the literal binary is placed alongside my solution for the reference path):

NuPack consuming add-package

And I’m done.  Pretty cool.  Any updates I (as the library author) would just update my .nuspec file to the new version, generate a new package, and publish it again.  The app developer can initiate update-package MyNuLibrary and get the updated bits.

Real world – Silverlight Toolkits

While my above exercise was interesting and demonstrated that NuPack could be used for Silverlight projects as well (ahem, SilverlightContrib) I thought I’d explore a more useful sample.  I took the Silverlight Toolkit binaries and packaged them up for NuPack.  Here was my manifest:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
   3:     <metadata>
   4:         <id>SilverlightToolkit</id>
   5:         <version>4.0.40413.2020</version>
   6:         <authors>
   7:             <author>Microsoft</author>
   8:         </authors>
   9:         <language>en-US</language>
  10:         <description>Silverlight Toolkit providing a set of controls</description>
  11:     </metadata>
  12:     <files src="Binaries\System.Windows.Controls.Toolkit.dll" target="lib\SL4" />
  13:     <files src="Binaries\System.Windows.Controls.Input.Toolkit.dll" target="lib\SL4" />
  14:     <files src="Binaries\System.Windows.Controls.Layout.Toolkit.dll" target="lib\SL4" />
  15:     <files src="Binaries\System.Windows.Controls.Data.Toolkit.dll" target="lib\SL4" />
  16:     <files src="Binaries\System.Windows.Controls.DataVisualization.Toolkit.dll" target="lib\SL4" />
  17: </package>

Since the source code is available for the toolkit I just used that base (hence the “Binaries”) folder name in the manifest.

The end result is me being able to include the Silverlight Toolkit and referencing it in one step rather than downloading, installing the MSI, and adding references.  Here’s a quick video of how simple that was:

Get Microsoft Silverlight

Awesome huh?  Now I could (and probably should have) actually made these independent packages so you could only get the Visualizations if you didn’t need anything else…and then could use the dependency feature of NuPack if needed.

Notice how I also did the Silverlight for Windows Phone Toolkit as well and that it automatically added the Icons for the ApplicationBar in my project as well.  That was due to a helpful tip from Phil about naming conventions in the package.


I think NuPack is pretty cool  Yes, flame away that it is nothing new conceptually.  That’s fine.  However the interation into the tool I use most is great and that I don’t have to go to a different console window and then back and forth.  That level of integration is pretty slick.

Will the Silverlight Toolkit(s) be deployed like this?  Who knows, right now it is just an experiment.  But it was pretty cool to see it all working as expected.  I think for an alpha view of the process it’s pretty good.  Oh and if you want IntelliSense on the nuspec file, they’ve published the schema so you can put that in a text file (nuspec.xsd) and place it in your %ProgramFiles%\Microsoft Visual Studio 10.0\Xml\Schemas directory.  Then notice my xmlns that I have in the snippets above?  Adding that will give you IntelliSense on the file format.

Hope this helps!

| Comments

Recently our team released a service release for Silverlight on 1-Sep-2010.  We affectionately call these “GDR” releases (general distribution release).

NOTE: Other teams have different names for different things.  I’m not sure why Microsoft doesn’t have a standard on these things and it’s funny to hear marketing teams argue the benefit of one name over the other.  For what it is worth, in my eyes, if it isn’t a major milestone release (or at least a ‘dot’ release [4.0->4.1 for example] then it is a service update.  Call it a GDR, Wave X, Service Pack, R2, blah blah.

In the GDR1 release (version 4.0.50826.0) we also released an update to the SDK.  This is where it can start get confusing…allow me to attempt to explain.  GDR1 released:

  • Runtime
  • SDK

On top of that when the release of the Windows Phone Developer Tools released, they shipped the GDR1 bits (and SDK) with them…so while there was no real “tool” update (outside of the required updates for WP7), them shipping the update in the tools effectively put all the new bits on your machine if you installed it.  Most of the time GDR releases are runtime-only releases.  Putting out an SDK release can have some consequences (beneficial if you need the update) as some of you have seen.

The Problem

Here’s what people are seeing…

Hey my users are getting messages to upgrade their Silverlight runtime when my app says minRuntimeVersion=”4.0.50401.0” – what gives?!  I thought this thing was supposed to work!?!?

Every time someone asks me about this, my first question is if they’ve installed the updated SDK.  Almost all the time the answer is yes.  And that is where the issue is (as also they’ve recompiled their app).  Along with the minRuntimeVersion, within the XAP the AppManifest.xml file there is also a RuntimeVersion attribute stamped for the app.  Both of these versions are being set by the version of the SDK.  So when you install a new SDK, that version is the value used here.

NOTE: Your <object> tag minRuntimeVersion isn’t updated on existing projects, but check on a new project and you’ll see it updated there.

So even though you might have specified in the <object> tag minRuntimeVersion for RTW (4.0.50401.0), the fact that the XAP is demanding (via the AppManifest) a later version is what is causing the conflict.

The problem exists when you either want (or have no choice because of auto-updates you subscribe to) the latest Silverlight runtime but as a developer, need to maintain applications and do not want to drive the user to an upgrade to the latest bits.

The Solution

If you find yourself in this situation and don’t want to keep manually changing the AppManifest attribute after each build and re-packaging the XAP, then you can do one thing to keep your environment the way you expect it.

To be clear, what I am outlining here will: enable you to have the latest Silverlight runtime while still building against the RTW of Silverlight 4.

First, you can uninstall Silverlight 4 GDR1 SDK (you can do this via the Add/Remove Control Panel).  Once you’ve done that you can install the Silverlight 4 RTW SDK (4.0.50401.0).

Now you’re done :-).  If you have apps that have been in this problematic state you’ll likely have to do some cleans on your build to ensure that the AppManifest is overridden correctly now.

Future Updates

As I mentioned, generally speaking the GDRs are runtime-only releases.  And actually we issued a second service release in September (GDR2 – 4.0.50917.0). 

NOTE: This GDR2 update has NO OTHER fixes than the issue mentioned in the KB article.  This was fixing a regression that prevented an app from loading/updating.  No other fixes are in this build at this time.

In these cases as a developer all you need to do is ensure you have the latest developer runtime to ensure you can debug, etc.  The Windows developer runtime can be obtained here.


If you’re in the situation as described above where your users are seeing a prompt that is not expected, you can easily modify your dev environment to prevent this.  The simple summary is:

  • Keep up-to-date on the latest developer runtime
  • Have the RTW SDK installed

The consequences here are if you are doing development with LightSwitch (which requires the updated SDK).

I’m not sure if my rambling here helps, but I tried to just say it how it is.

Hope this helps!

This work is licensed under a Creative Commons Attribution By license.

| Comments

It looks like the MSDN team has arranged some deep dives into Windows Phone development across the country.  I am sure that for Microsoft developers Windows Phone 7 represents a new opportunity to get out in the marketplace with your XAML skills and get recognized (paid) for your work!  It has been exciting to see a lot of interest from Silverlight developers in Windows Phone 7.

Windows Phone 7

If you are one that hasn’t had the time to soak in the platform or simply haven’t been paying attention, you are in luck.  There are a series of launch events happening across the country, most of which involve a 2-day training (free) for Windows Phone 7.  Here’s what I could gather for the basic agenda for these 2-day workshops:

  • Day 1: Intro, sensors, application lifecycle, tiles, application bar, connecting to services, recording audio, capturing pictures, design guidelines, game development with Silverlight/XNA, etc. – all the fundamentals to get started
  • Day 2: turn your vision into an app, learn about (and submit your app) the Marketplace…or continue learning using a bunch of hands-on labs and the tools to write applications in Silverlight and XNA

It seems like a pretty good deal and you should check them out.  Bringing your own laptop is encouraged and the tools are free!  Check out these opportunities in a city near you – aside form the 2-day events there also looks to be some 1-day sessions as well in some areas.  Check out all the details at http://www.msdnevents.com/wp7 and register for a location near you!

| Comments

I was spelunking around playing with Silverlight in Windows Phone 7 and specifically the CameraCaptureTask.  The “tasks” are APIs that allow you to interact with phone-specific functionality like the camera, picture picker, phone dialer, etc.  A whole list of the available tasks in the Microsoft.Phone.Tasks namespace can be found in the developer documentation.

I was basically creating a simple application that would allow you to choose (PhotoChooserTask) or take a picture (CameraCaptureTask) and then display the picture (and later post it online or something).  Here was my basic XAML structure:

   1: <Grid x:Name="LayoutRoot" Background="Transparent">
   2:     <Grid.RowDefinitions>
   3:         <RowDefinition Height="Auto"/>
   4:         <RowDefinition Height="*"/>
   5:     </Grid.RowDefinitions>
   7:     <!--TitlePanel contains the name of the application and page title-->
   8:     <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
   9:         <TextBlock x:Name="ApplicationTitle" Text="PICTURE POSTER" Style="{StaticResource PhoneTextNormalStyle}"/>
  10:         <TextBlock x:Name="PageTitle" Text="take and post" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
  11:     </StackPanel>
  13:     <!--ContentPanel - place additional content here-->
  14:     <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  15:         <Image Margin="8,8,8,159" x:Name="ChosenPicture" />
  16:         <Button Click="OnPostClicked" x:Name="PostPic" Content="POST" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,8,47" Width="199"/>
  17:         <TextBlock x:Name="PostedUri" TextWrapping="Wrap" VerticalAlignment="Bottom" Margin="8,0,8,20"/></Grid>
  18: </Grid>
  20: <!--Sample code showing usage of ApplicationBar-->
  21: <phone:PhoneApplicationPage.ApplicationBar>
  22:     <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
  23:         <shell:ApplicationBar.MenuItems>
  24:             <shell:ApplicationBarMenuItem Text="take picture" Click="OnMenuTakeClicked"/>
  25:             <shell:ApplicationBarMenuItem Text="choose picture" Click="OnMenuChooseClicked"/>
  26:             <shell:ApplicationBarMenuItem Text="save picture" Click="OnSavePictureClicked" />
  27:         </shell:ApplicationBar.MenuItems>
  28:     </shell:ApplicationBar>
  29: </phone:PhoneApplicationPage.ApplicationBar>

And the initial code to trigger the task from a MenuItem:

   1: private void OnMenuTakeClicked(object sender, EventArgs e)
   2: {
   3:     CameraCaptureTask cam = new CameraCaptureTask();
   4:     cam.Completed += new EventHandler<PhotoResult>(OnCameraCaptureCompleted);
   5:     cam.Show();
   6: }
   8: private void OnMenuChooseClicked(object sender, EventArgs e)
   9: {
  10:     PhotoChooserTask pix = new PhotoChooserTask();
  11:     pix.Completed += new EventHandler<PhotoResult>(OnCameraCaptureCompleted);
  12:     pix.ShowCamera = true;
  13:     pix.Show();
  14: }

As you can see it is pretty simple and brings up the OS-standard camera and/or photo chooser. 

NOTE: if you use the PhotoChooserTask you can also initiate taking a new picture from that task as well.

After the picture is chosen (from a new pic or from a picker) I put the item in the Image control in my XAML:

   1: void OnCameraCaptureCompleted(object sender, PhotoResult e)
   2: {
   3:     capturedImage = e.ChosenPhoto; // this is a member variable to store the last chosen pic
   5:     BitmapImage bmp = new BitmapImage();
   6:     bmp.SetSource(e.ChosenPhoto);
   8:     ChosenPicture.Source = bmp;
   9: }

During this, however, I found that no matter how I held the phone when I took the picture (portrait or landscape), the API always assumed landscape.  I tried looking at some of the device orientation data, but it wasn’t providing the right information at the time I needed it.  It turns out after some internal discussions that others were seeing this as well.  On the device I have (Samsung) the picture snapshot button is in a natural place if you were to hold it landscape.  However, in my current experience with my mobile devices (Android and iPhone) I actually take more pictures in portrait mode.

After some discussion with folks internally, one of our test leads for WP7 reminded everyone that the phone does provide the EXIF information for each picture taken.  One of the attributes of EXIF is orientation (or rotation).  Now all we needed was a method to read the EXIF data in .NET…enter ExifLib.  This is a cool Code Project article and source code download that does a great job providing a simple EXIF reading experience.

The ExifLib as it stood wouldn’t work with the Stream that is provided as a result of the CameraCaptureTask, so a slight modification (or in my case I just created an override) to the function was needed for the library.  Here’s the additional override I added:

   1: public static JpegInfo ReadJpeg(Stream FileStream, string FileName)
   2: {
   3:     DateTime now = DateTime.Now;
   4:     ExifReader reader = new ExifReader(FileStream);
   5:     reader.info.FileSize = (int)FileStream.Length;
   6:     reader.info.FileName = string.Format("{0}.jpg", FileName);
   7:     reader.info.LoadTime = (TimeSpan)(DateTime.Now - now);
   8:     return reader.info;
   9: }

Now with that in place, I could accomplish taking my picture and read the EXIF data and apply the appropriate transform based on the orientation data.  The first thing I had to do was to create a RotateTransform on my Image element as well as set the RenderTransformOrigin on the Image element:

   1: <Image Margin="8,8,8,159" x:Name="ChosenPicture" RenderTransformOrigin="0.5,0.5">
   2:     <Image.RenderTransform>
   3:         <RotateTransform x:Name="ImageRotate" />
   4:     </Image.RenderTransform>
   5: </Image>

Now in code in my completed handler for the task I modified it to look at the EXIF orientation data and apply the correct rotation to show the image:

   1: void OnCameraCaptureCompleted(object sender, PhotoResult e)
   2: {
   3:     capturedImage = e.ChosenPhoto;
   5:     BitmapImage bmp = new BitmapImage();
   6:     bmp.SetSource(e.ChosenPhoto);
   8:     ChosenPicture.Source = bmp;
  10:     // figure out the orientation from EXIF data
  11:     e.ChosenPhoto.Position = 0;
  12:     JpegInfo info = ExifReader.ReadJpeg(e.ChosenPhoto, e.OriginalFileName);
  13:     PostedUri.Text = info.Orientation.ToString();
  15:     switch (info.Orientation)
  16:     {
  17:         case ExifOrientation.TopLeft:
  18:         case ExifOrientation.Undefined:
  19:             ImageRotate.Angle = 0d;
  20:             break;
  21:         case ExifOrientation.TopRight:
  22:             ImageRotate.Angle = 90d;
  23:             break;
  24:         case ExifOrientation.BottomRight:
  25:             ImageRotate.Angle = 180d;
  26:             break;
  27:         case ExifOrientation.BottomLeft:
  28:             ImageRotate.Angle = 270d;
  29:             break;
  30:     }
  31: }

Now I’ve got my flexibility in my application and don’t have to worry about the orientation. 

Now of course this only helps for the display of the information.  If you were to use the libraries to save the image you’d still have the issue of an incorrect orientation on the picture.  Again, iterating with our test team internally (thanks Stefan!!!) here’s a modified view of the world.

First, instead of rotating the Image element, let’s just rotate the actual Pixels themselves:

   1: void OnCameraCaptureCompleted(object sender, PhotoResult e)
   2: {
   3:     // figure out the orientation from EXIF data
   4:     e.ChosenPhoto.Position = 0;
   5:     JpegInfo info = ExifReader.ReadJpeg(e.ChosenPhoto, e.OriginalFileName);
   7:     _width = info.Width;
   8:     _height = info.Height;
   9:     _orientation = info.Orientation;
  11:     PostedUri.Text = info.Orientation.ToString();
  13:     switch (info.Orientation)
  14:     {
  15:         case ExifOrientation.TopLeft:
  16:         case ExifOrientation.Undefined:
  17:             _angle = 0;
  18:             break;
  19:         case ExifOrientation.TopRight:
  20:             _angle = 90;
  21:             break;
  22:         case ExifOrientation.BottomRight:
  23:             _angle = 180;
  24:             break;
  25:         case ExifOrientation.BottomLeft:
  26:             _angle = 270;
  27:             break;
  28:     }
  30:     if (_angle > 0d)
  31:     {
  32:         capturedImage = RotateStream(e.ChosenPhoto, _angle);
  33:     }
  34:     else
  35:     {
  36:         capturedImage = e.ChosenPhoto;
  37:     }
  39:     BitmapImage bmp = new BitmapImage();
  40:     bmp.SetSource(capturedImage);
  42:     ChosenPicture.Source = bmp;           
  43: }
  45: private Stream RotateStream(Stream stream, int angle)
  46: {
  47:     stream.Position = 0;
  48:     if (angle % 90 != 0 || angle < 0) throw new ArgumentException();
  49:     if (angle % 360 == 0) return stream;
  51:     BitmapImage bitmap = new BitmapImage();
  52:     bitmap.SetSource(stream);
  53:     WriteableBitmap wbSource = new WriteableBitmap(bitmap);
  55:     WriteableBitmap wbTarget = null;
  56:     if (angle % 180 == 0)
  57:     {
  58:         wbTarget = new WriteableBitmap(wbSource.PixelWidth, wbSource.PixelHeight);
  59:     }
  60:     else
  61:     {
  62:         wbTarget = new WriteableBitmap(wbSource.PixelHeight, wbSource.PixelWidth);
  63:     }
  65:     for (int x = 0; x < wbSource.PixelWidth; x++)
  66:     {
  67:         for (int y = 0; y < wbSource.PixelHeight; y++)
  68:         {
  69:             switch (angle % 360)
  70:             {
  71:                 case 90:
  72:                     wbTarget.Pixels[(wbSource.PixelHeight - y - 1) + x * wbTarget.PixelWidth] = wbSource.Pixels[x + y * wbSource.PixelWidth];
  73:                     break;
  74:                 case 180:
  75:                     wbTarget.Pixels[(wbSource.PixelWidth - x - 1) + (wbSource.PixelHeight - y - 1) * wbSource.PixelWidth] = wbSource.Pixels[x + y * wbSource.PixelWidth];
  76:                     break;
  77:                 case 270:
  78:                     wbTarget.Pixels[y + (wbSource.PixelWidth - x - 1) * wbTarget.PixelWidth] = wbSource.Pixels[x + y * wbSource.PixelWidth];
  79:                     break;
  80:             }
  81:         }
  82:     }
  83:     MemoryStream targetStream = new MemoryStream();
  84:     wbTarget.SaveJpeg(targetStream, wbTarget.PixelWidth, wbTarget.PixelHeight, 0, 100);
  85:     return targetStream;
  86: }

Notice here that OnCameraCaptureCompleted is different in that we first check the orientation and if needed rotate the pixels using the newly introduced RotateStream method.  The resulting stream is what we actually set on our Image element and no need for RotateTransform at this point.  I can then even have a menu item save the picture to the media library on the device:

   1: private void OnSavePictureClicked(object sender, EventArgs e)
   2: {
   3:     if (capturedImage != null)
   4:     {
   5:         capturedImage.Seek(0, 0); // necessary to initiate the stream correctly before save
   7:         MediaLibrary ml = new MediaLibrary();
   8:         try
   9:         {
  10:             Picture p = ml.SavePicture(Guid.NewGuid().ToString(), capturedImage);
  11:             PostedUri.Text += ":" + p.Name;
  12:         }
  13:         catch (Exception ex)
  14:         {
  15:             PostedUri.Text = ex.Message;
  16:         }
  17:     }
  18: }

The MediaLibrary is a part of Microsoft.Xna.Framework.Media and provides the easy functionality of saving to the device.  And upon sync (or sharing) my image is what I expected when I took the picture using the CameraCaptureTask.

Here is my final project sample (lots of debug code in there, but you should get the point): WindowsPhoneApplication63.zip (requires the Windows Phone Developer Tools)

Hope this helps!

| Comments

On the heels of the Windows Phone Developer Tools and Silverlight for Windows Phone Toolkit releases I saw a lot of exhaling going on in the hallways today.  Apparently Jeff saved his largest one for an avalanche of knowledge on Twitter in the late afternoon.  Jeff Wilcox is a developer on the Silverlight team and has been working on the Silverlight for Windows Phone initiative as well as the toolkit released today.  He was headed out on vacation but decided to throw out some words of wisdom for Windows Phone developers working in Silverlight.  Here’s some of those nuggets – I wanted to capture them before he left because who knows how long they’ll stay in Twitter.  It reads like a true guide to developing great apps on Windows Phone…

  • Panorama looks nice, but Pivot will offer faster start time.
  • You can also set a Background image to a Pivot. You won't get the parallax effect, but it is another option.
  • Be aware of how many pano and pivot items you do have. Memory expands quick when you have a lot of views and images!
  • Even if you have a 30k compressed JPEG image, at runtime that becomes an uncompressed surface that may take several MBs of memory
  • Pivot and Panorama can have UI element headers and titles, too, but you'll need to apply your own styling (fonts and sizes)
  • Beware that UI elements larger than 2000x2000 pixels that are bitmap cached clip on Windows Phone 7. We know it isn't perfect, but beware k?
  • Setting SelectedIndex before the items are set on a Pivot causes an exception. Wrap in a try/catch or wait for loaded (sorry!)
  • A slideshow app in 5 minutes: Pivot with null Header and Title and item headers. Beware memory use though.
  • A lot of people try building 'wizard' screens with panorama & pivot control. Please don't do this! Thx, the "UX gods"
  • Layout is a killer. But like death, you eventually have to pay it for everything.
  • So consider delay loading controls and screens. A Panorama with a billion items will take forever to load due to layout.
  • If you're not using PerformanceProgressBar, I'll send @JustinAngel after you ProgressBar for Windows Phone 7
  • If your app rocks and starts really quick *on a device* consider not using a splash screen
  • It's true. Your 6-core machine running the wp7 emulator is NOT indicative of device (single core!) performance. Beware!
  • We've talked perf before... Content over Resources for images means fast startup time http://bit.ly/9DhVbd
  • If you're using Panorama, a Resource background will load immediately compared to Content
  • Remember that for ingestion to the marketplace, your apps need to consume under 90MB of memory
  • However on devices with > 256MB, its cool to use more *in those cases
  • long deviceTotalMemory = (long)DeviceExtendedProperties.GetValue("DeviceTotalMemory");
  • long applicationCurrentMemoryUsage = (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
  • long applicationPeakMemoryUsage = (long)DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage")
  • Your compositor thread should rock out around 60fps all the time please. #wp7dev perf counters: http://bit.ly/busJIi
  • If your UI thread gets pegged, your compositor thread will suffer... remember your BackgroundWorker kids!
  • Unfortunately the "Analytics" type from desktop Silverlight is not on the phone today, so you can't measure CPU in a regular app
  • Having a single DispatcherTimer in your app can affect your battery life regardless of interval. Chose wisely
  • You can set the Foreground property on Pivot to set the title and header text color
  • Using a map control inside a Panorama or Pivot is not recommended for a number of UX and technical reasons. Navigate to a subpage.
  • Please use text styles and never hardcode sizes or default fonts.
  • The panorama/pivot items expect most their contents to have a 12px margin left/right for UX reasons. The default styles have this.
  • So if you have something in a pano/pivot item with 0 margin & padding, your UX will be funky
  • Fill rate is super important. Keep it under 2.5 please
  • What is fill rate? 1.00 means one screen of pixels being rendered every frame.
  • Check your apps for extra, un-needed background colors on pages, controls, etc.. They impact perf.
  • That sexy "tilt" effect? Use Peter's behavior http://bit.ly/90Z1yR and/or check out the MSDN docs
  • DataTemplates with a bunch of StackPanels and Grids? Try to simplify to a Grid with the right col/rows instead for perf wins.
  • Unit testing in a quick and dirty way is possible on the Windows Phone thx to the sl unit test fx. http://bit.ly/a0DWah
  • Only use Dispatcher.BeginInvoke when you must. Look at SmartDispatcher (ps old code sry) http://bit.ly/axHh36
  • For a "wide" Panorama item, set the item's Orientation to "Horizontal"
  • Play with the cache and redraw vis. settings to see what's being cached in your app http://bit.ly/busJIi
  • Things in a list/scroll viewer are often automatically bitmap cached by Silverlight for Windows Phone runtime
  • If you have a progress bar with IsIndeterminate="True" in your app, even if its hidden those storyboards are costly! Set to False!
  • We did work on Windows Phone 7 to move more networking to the background thread - hope it helps
  • When a Panorama loads, all its items go through a render pass. For pivot, it is done incrementally for neighboring items.
  • When making web requests, see if the service lets you scope down the fields that are returned for quicker perf (and JSON over XML!)
  • If you navigate to a subpage, the old page will stick around - so complex pano/pivot pgs stay in memory unless you're proactive
  • The "app deployment tool" installed with the dev tools lets you run others apps in emulator/device without needing source
  • We optimize for loading some things from isolated storage. Images from an isostore stream may load faster than a MemoryStream
  • If your source files have "Black" or "White", you might be doing it wrong. PhoneForegroundBrush, PhoneBackgroundBrush instead!
  • If your control's dep. property has a change handler, animating that prop. will always happen on the UI thread (no gpu accel.)
  • Animating Opacity on a CacheMode="BitmapCache" element = compositor thread (GPU!)
  • In the RTM tools, scroll viewers all have the "bounce" effect automatically
  • If you ignore the phone's theme (and go all light bg, like the mail app), your scrollbarsmust be retemplated or you won't see them!
  • Although data binding is not evil, an observable col. with a complex data temp. and 200k items is evil.
  • The web browser control won't let you NavigateToString until it has loaded.
  • If you have an app with a lot of different web browser controls, think about consolidating to one, so it only has to load once.
  • Panorama is designed to be a starting place. Think whitespace. Not tons of data
  • Free performance win: when you use Panorama the way the UX guidelines recommend, it is faster! http://bit.ly/9zTxtU
  • Those theme xamls for #wp7? Yeah they are in %ProgramFiles%\Microsoft SDKs\Windows Phone\v7.0\Design\
  • Resist the urge to Panorama every app.  It is a sweet UX thing when used right…but not just because.
  • Resist the urge to iPhone gradient your apps. Think outside the box! Also it avoids color banding...

So as you can see, he sort of knows what he’s talking about :-).  Go subscribe to his blog and follow him on Twitter.  Other helpful links:

Thanks Jeff for sharing your awesomeness!

Hope this helps!