| Comments

I saw a note on Twitter come through yesterday about building pixel shaders for Silverlight in Visual Studio.  The question, from @blamborn was:

Does anyone know if there is a Shader Effects BuildTask like the #WPF one here http://bit.ly/gCkNT only for #Silverlight?

I replied that you can use the same build task and thought I had written my work around on how to do that, but apparently I never clicked published somewhere along the line.  I recall doing some work for this for a presentation a while back on VSLive and started to write it up.  Here are hopefully some helpful knowledge around the subject.

Option 1: Compiling using DirectX Libraries

One option you can do is to use a pre-build command for your project in combination with the DirectX SDK.  What you really need out of the DirectX SDK is a tool called fxc.exe.  This is a compiler for the HLSL code for the pixel shader code you write.  The download is 500MB.  Yeah, big.  Once you have it installed you can put a pre-build command on your Visual Studio project with something like this:

   1: "C:\Program Files (x86)\Microsoft DirectX SDK (March 2009)\Utilities\bin\x86\fcx.exe" 
   2:     /T ps_2_0 /Fo "$(ProjectDir)ShaderEffect1.ps" "$(ProjectDir)ShaderEffect1.fx"

Now keep in mind your installation directory might be different, but you should get the gist of it.  The result of this command is that you’ll get a <Filename>.ps file that you will include in your project.  The .ps file, in fact, *IS* your pixel shader…that’s really the only thing your Silverlight project cares about!  The build command doesn’t automatically make it a part of your project though, so you’ll have to ensure that after it builds you use the “show all files” feature in Visual Studio and include it in your project.

Option 2: Using a build task

Another option is to use a build task in your project.  For Windows Presentation Foundation (WPF) developers, this is what is easiest and doesn’t require the massive 500MB download.  What is cool is that we can use the same build task for Silverlight.  Awesome right?  Well, yes and no.

It’s awesome that it works.  What is not awesome is the setup.  Allow me to walk you through my steps.  It’s a bit of hackery admittedly.  If anyone is a super Visual Studio extensibility wizard and wants to contribute knowledge to me on extending it to my liking, I’ll send you a virual Mt. Dew!  Here’s my steps otherwise.

Install the WPF Build tasks!!!  You can get them from here (Shader Effects Build Task and Templates).  Run the installer.  These steps assume you have them already!!!  You don’t need the templates, but if you want you can install them too – they are for WPF.  You’ll install my template as the next step.

1. Get the VS template I created for Silverlight Pixel Shaders.

I created an item template for Silverlight projects so that you can do Add Item…Silverlight Pixel Shader to your project.  Once you have this installed (just double-click on the VSI and follow the steps) you will see that option.

The template provides you with the right files that get added as well as follows the requirement for a pixel shader being a resource in your project.  You’ll notice after you add the item (let’s say you kept the default name) that the code will show:

   1: static ShaderEffect1()
   2: {
   3:     _pixelShader.UriSource = new Uri("/SilverlightApplication29;component/ShaderEffect1.ps", UriKind.Relative);
   4: }

This is the best practice for a pixel shader.  Note that my project name is SilverlightApplication29, but the template will use your projects name, of course.

2. Hackery 1: Open the **proj file for your Silverlight application in a text editor like notepad.

Yes, I know.  You are cursing me now.  I agree, if the tool (VS) is awesome why do I have to do this step?  I’ve been trying to understand myself how an item template can actually do all these things for you…again, if you are an extensibility expert, let me know!  But for now, bear with me.

In the **proj file (csproj or vbproj) add the following information right after the Import node in the file:

   1: <ItemGroup Condition="'$(BuildingInsideVisualStudio)'=='true'">
   2:     <AvailableItemName Include="Effect" />
   3:   </ItemGroup>
   4:   <UsingTask TaskName="ShaderBuildTask.PixelShaderCompile" AssemblyName="ShaderBuildTask, Version=1.0.3072.18169, Culture=neutral, PublicKeyToken=44e467d1687af125" />
   5:   <Target Name="EffectCompile" Condition="'@(Effect)' != '' ">
   6:     <PixelShaderCompile Sources="@(Effect)">
   7:       <Output TaskParameter="Outputs" ItemName="Resource" />
   8:     </PixelShaderCompile>
   9:   </Target>
  10:   <PropertyGroup>
  11:     <!-- Add effect compilation to the PrepareResourcesDependsOn so that the effect fx  get compiled before resources are collected -->
  12:     <PrepareResourcesDependsOn>EffectCompile;$(PrepareResourcesDependsOn)</PrepareResourcesDependsOn>
  13:   </PropertyGroup>

These properties enable you to see the “Effect” build task in the properties for your .fx files in the project.  Save the proj file and go back to Visual Studio.  You should be prompted to reload – go for it.  You may see a file named Effect with a warning icon on it.  For now, ignore it.  It’s admittedly an artifact of this hackery.  It will look like this:

Project solution explorer

Now that you have both of these steps you can add a new pixel shader (using my template of course).

3. Change the .fx file’s properties to Build Action: Effect.

Once you have added a shader library (using my template) to the project, select the .fx file (which is your HLSL code) and look at the properties dialog for the file.  Change the build action to the new item Effect.

Build action property

Build your project.

4. Hackery 2: Refresh the view all files and add to project

After the build, refresh the view all files feature and you should see your .ps file.  Right click on that and say Include in Project.  It should be included as a Resource build action automatically, but if it isn’t, make sure it is.

That’s it (as I say with a snarky smile).  I know it seems like some hacking….and it is.  But if you don’t want to download the DirectX SDK, you can still re-use the WPF build taks with a little project file modification.

If you don’t have a clue what a pixel shader is, check out this video.

Hope this helps!

| Comments

Small public service announcement here for Silverlight developers…we released a minor update to the runtime and SDK today.  You may see this referred to as Silverlight 3 GDR2.  Formally it is version 3.0.40818.0.  Usually when a release pops up people are curious about what is in it and why there wasn’t some major announcement.

NOTE: “GDR” is Microsoft-speak for general distribution release.

If you are interested in the full release history for Silverlight it is always available online (click the deployment section).  This latest minor release really had several updates for improving some media and network scenarios, but namely the main driver on this release was GB18030, which is a Chinese government standard for support of its national characters.  The standard reads:

“All products currently sold or to be sold in China must plan the code page migration to support GB18030 without exception. GB18030 is a "mandatory standard" and the Chinese government regulates the certification process to reinforce GB18030 deployment.” source: http://gb18030.org

So yeah, we needed to do this :-).  Silverlight made necessary changes to ensure that Blend and Visual Studio would be in compliance.

How will this affect my users?

Most likely it won’t unless you know about the fixes and have been a customer of one of the needs.  The driver for your customers/end-users of your applications is the minRuntimeVersion attribute of the plugin.  This is what tells the plugin on the end user machine what version they must have at a minimum to support your application.  Unless you change this to 3.0.40818.0 then your app isn’t likely to be affected.

If you need the update to comply with GB18030 deployments for example, then you’d want to modify the minRuntimeVersion to suit your needs.  This would prompt users with an older version to install the update.  You can make this a better experience for them by following the guidance in the Silverlight Installation Experience whitepaper (you can see a very simple implementation of this here).

What about my development environment?

For your development environment, you’ll need to upgrade to the latest development runtime.  If you try to install the end-user runtime on a developer machine, you will not be successful.  This is like trying to upgrade your turbo car engine with the standard V8.  The developer runtime is a superset so you can only upgrade to another developer runtime.

The links available at the Silverlight Community Site in the Get Started section are still valid (see developer runtime links at the bottom of that page for Windows and Mac).  There are no new Silverlight Tools update you need to install at this time.  There were some minor updates to the SDK which aren’t required, but again, if you want the latest, you can always get it.

That is all.  This is not a major update, but if you’re like me and can’t stand having anything but the latest, you can go out and get it.  Note that all your current installer links for your end users will remain to work always.  We update our installation handlers on our sites each release to be appropriate.

These updates went live approximately 10:00AM PST.  There may be some still propagating across the giant tubes of the interwebs.

Hope this helps!

| Comments

One of my colleagues, Corrina, pointed me today to some research her team is conducting on user experience with Visual Studio and Silverlight.  From her post today,

…that my co-workers on the Visual Studio User Experience Team are doing focused on professional and non-professional web/application developers. The research involves…

  • A short survey
  • A telephone interview to discuss your development experiences

A gratuity will be offered to individuals selected to participate in the telephone interview, and selection for interviews is based on responses to the survey. The gratuity is either a $25 Visa/American Express gift card or select Microsoft software titles.

If you are a professional or non-professional web/application developer and would like to be involved in this research, please check out her post for more information at the top.

Also Corrina is the one who has been creating the application template themes I’ve posted about.  We’ve been talking more about how people use themes that we provide as well as the Silverlight Toolkit themes.  Corrina’s looking for some insight as well:

One last thing, I have a few theme questions that I would appreciate your feedback on...

  • Do these themes help you get started building your applications (act as inspiration, learning tools...), and, if not, why not?
  • Do you use these theme's 'as is' with little modification when building your applications?
  • What type of themes would you like to see in the future (what would help you be more productive when building your applications)?

Check out her post and participate in the feedback if you can!

| Comments

I normally hate short posts – but my friend John Papa put a poll up that I wanted to make sure gets some attention…please provide your opinion on this poll:

What kinds of samples would you most like to see in a community Silverlight code sharing site?

Help John out shape something useful!

| Comments

While I was at the Silverlight Atlanta Firestarter event I had a chance to meet some great people.  One of them was Sergey Barskiy.  Sergey was doing a session on deployment and while in the speaker area we were chatting about overall feedback on Silverlight.  One of the things he mentioned was what he thought was a bug in Visual Studio Tools for Silverlight.  It was around RESX files and the modifier setting (Internal, Public, etc.).  More on that later.  Sergey was using RESX files for localization.  While investigating the bug for him, I realized how many people might not know how to do some simple string localization/binding in their Silverlight applications.  It’s relatively simple and I thought I’d outline the steps.  I must admit that I’ve never had to develop a full-fledged internationalized application before, and I applaud those who have and have tackled both the obvious of the language localization challenges as well as cultural and display challenges with various technologies.

The Sample Application

For this experiement we’ll keep it simple and we’re talking about String localization.  We are going to work with a Silverlight 3 application in simple form which will have a TextBlock and a Button control which will have their respective Text and Content settings.  Here’s the English-US (en-US) version of the app:

Sample application image

If we were like most applications we’d be done…but this post will try not to be so US-centric :-).

Step 1: Adding default String resources

As a best practice for String resource localization, in your Silverlight application structure, organize your resource files accordingly.  We’re going to use the RESX file approach and let the framework do most of the work for us.  To that regard in my application I have created a folder called Resources and will be placing my RESX files there.  I’ll first add the default set of data, adding a new RESX file named Strings.resx.

When you add this you’ll notice you get a Strings.resx and a designer (cs or vb) file.  By default the Resource Designer will open and we’ll add two values: WelcomeMessage and ButtonMessage.  Our surface looks like this:

Visual Studio resource designer

Notice the Access Modifier section in the designer.  We need to set this to Public in order to use it in binding we will do later, so set it now.  We save this and step one is done.  Let’s test it out and use it.

Step 2: Binding our RESX files to our XAML

Now that we have a Strings.resx file (which is marked as Embedded Resource by default) in our Resources folder, we can use it as a resource in our XAML.  We need to do a few things to enable this.  First, we need to add an XML namespace to our XAML page.  I’ve chosen local for mine, but choose whatever.  We will have this namespace point to our Resources namespace in our application.  Mine looks something like this:

   1: xmlns:local="clr-namespace:StringLocalization.Resources"

Now I can use that assembly classes in my XAML.  I’ll add the Strings class to my UserControl’s Resources section giving it a key of LocStrings like this:

   1: <UserControl.Resources>
   2:     <local:Strings x:Key="LocStrings" />
   3: </UserControl.Resources>

And now I have a XAML resource I can bind to.  Let’s do that.

Step 3: Binding to the resx file data

My XAML has a TextBlock and a Button I want to bind to the string values.  Because I have a XAML resource this is simple and I just create a binding using the the XAML binding syntax to that resource.  here’s what my TextBlock and Button look like (relevant portions):

   1: <TextBlock Text="{Binding Source={StaticResource LocStrings}, Path=WelcomeMessage}" />
   2: <Button Content="{Binding Source={StaticResource LocStrings}, Path=ButtonMessage}" />

Notice that I’m using a Path that points to the String name in the RESX file.  Let’s run the application now and we should see what we’re looking for right?

WRONG.  Here’s the bug that Sergey was talking about.  It turns out that there does seem to be a bug in Visual Studio with regard to the modifier, and more specifically the PublicResXFileCodeGenerator custom tool that is used to generate the code.  It turns everything in the resource to public except the constructor.  Not having the constructor public (it is still marked internal) is what is causing our problem. 

NOTE: This is not a bug in Silverlight or in the Silverlight tools, but more widely in Visual Studio.  The same thing reproduces on any VS project type.  It’s been noted and is being tracked to address.  See this topic:

What we have to do as a workaround is to go into the Strings.designer.cs file manually and change the constructor from internal to public.  I will note that each time you open up the designer for that main resource file that it may get reset to internal, so remember that. 

After changing to public you can run the application and get the desired output.  There is actually a way to still use the resources and keep the constructor internal, but it involves using another class you have to create and instantiate…more on that later.

Step 4: Adding additional localized resources

Now that we have our base working, we simply need to provide the localized resources for our application.  I’m choosing to localize in Spanish (es-es), French (fr-FR), German (de-DE) and Japanese (ja-JP). 

I solicited some help on Twitter to get some “human” translation to double-check my translated text with Windows Live Translator.  To my surprise, Windows Live Translator actually did a great job.  I think with short sentences it is fine, but longer conversational text may lose context.

Thanks to: Ken Azuma, Othmane Rahmouni, Talya, Misael Ntirushwa, Hannes Preishuber, and others who jumped in to offer help for my little sample.

To do this we simply add more resource files into our project following a specific naming scheme.  Since we have Strings.resx when we add additional languages we’ll add them as Strings.<locale>.resx.  So adding German would be Strings.de-DE.resx and so forth.  It really is best to use the xx-xx locale settings versus just the two character (i.e., de) ones.  Note when you add these files to the project that your modifier section in the resource designer should say No Code Generation automatically.  If it doesn’t, choose that option.  We only need the code for our default language choice.  When I’m done my structure looks like this:

Project structure with localized string resx files

Obviously the contents of the file contains the localized string information.  Note that the string parameter names still have to match.  So even though I’m localizing the contents of WelcomeMessage, in the German resource the parameter is WelcomeMessage and not Wilkommen or something like that.

UPDATE: Forgot one critical step – oops.  You have to manually edit your **proj (csproj/vbproj) file for your Silverlight project to add the locales in the SupportedCultures node of the proj file.  This is a manual step (and sucks), but don’t forget it or nothing will work as you suspect.

Step 5: Testing it out.

Because we used our declarative binding in Steps 2-3, we don’t have to change our code.  We should test it out to make sure it works though.  There are two good ways I have found to test this out.  First, the Silverlight plugin supports forcing a UICulture and we could do it that way.  Let’s test German.  In our plugin instantiation on our hosting page we’ll add these two parameters to the <object> tag (relevant portions):

   1: <object ...>
   2:     ...
   3:     <param name="culture" value="de-de" />
   4:     <param name="uiculture" value="de-de" />
   5:     ...
   6: </object>

That tells the plugin to load with those cultures set.  You can change them without recompiling your application and the language will change.

A second option would be to actually change the display language of your Windows environment.  For some this may be a little frightening as your screen suddenly may change to a language you don’t understand natively.  I recommend if you go this route to keep a translation dictionary handy find your way back (you have to logoff/logon to change a display language)!

Either way when you run the application using either of these methods you should get what you expect.  Here’s my German output:

German localized sample application

Obviously it is easier to test using the <param> approach (changing display languages in Windows requires a logoff), but ultimately I recommend doing actual OS display language for verification.  If a user is on a language culture that you have not localized, it will use the default values provided in our initial Strings.resx file.

Another option for testing is that you can actually change the culture and UICulture values using the Thread namespace APIs in Silverlight.  Keep in mind though changing these values on the CurrentThread does *not* reload the default resource, so you’d have to do some additional code to get the resource to load using that new culture setting.

Caveats and cultural differences

One thing to note is that while your application may now have an easy ability to display localized string data with simple bindings, it may not always be appropriate.  Take for instance Japanese language translation (I’ll assume it is roughly translated correctly, but for the sake of this discussion this serves a purpose) of “Click here” for the button.  In English it fits fine in our lovely world of fixed width button sizes.  But look at the Japanese translation of the text:

ここをクリックしてください。

And here’s how it looks in the button:

Japanese localized sample output

Notice that we don’t see all of the characters?!  These are things that you’ll have to understand when things seem simple enough.  Sometimes translated strings will be longer/shorter than your intended design.  Designing around a localized approach will have to consider these in advance.  In fact, for some languages you may have an alternate placement of controls even to accommodate the culture.

As I noted that I used machine translation for my sample here, but I do want to stress that I think respecting cultural differences is important in customer facing applications.  Using something like Windows Live Translator seems simple enough and might work in simple instances, but I would recommend hiring true localization resources/people to help you differentiate the subtle differences in languages.

Public modifier workaround and dynamically setting the culture

As I mentioned above there is a workaround to the internal/public modifier bug.  It is easy enough to change of course, but you may want to look at this approach as well…and in some implementations this may fit within your model better.  The idea is to provide your own class implementation access to the Strings resource.  Here’s an example of what my custom class implementation might look like:

   1: public class CustomResources : INotifyPropertyChanged
   2: {
   3:     private static StringLocalization.Resources.Strings _strings = new StringLocalization.Resources.Strings();
   4:     public StringLocalization.Resources.Strings LocalizedStrings
   5:     {
   6:         get { return _strings; }
   7:         set { OnPropertyChanged("LocalizedStrings"); }
   8:     }
   9:  
  10:     #region INotifyPropertyChanged Members
  11:  
  12:     public event PropertyChangedEventHandler PropertyChanged;
  13:  
  14:     private void OnPropertyChanged(string propertyName)
  15:     {
  16:         if (PropertyChanged != null)
  17:         {
  18:             PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  19:         }
  20:     }
  21:     #endregion
  22: }

And then my resource section would look like this:

   1: <UserControl.Resources>
   2:     <localCustom:CustomResources x:Key="CustomLocStrings" />
   3: </UserControl.Resources>

And my binding would look like:

   1: <TextBlock Text="{Binding Source={StaticResource CustomLocStrings}, Path=LocalizedStrings.WelcomeMessage}" />
   2: <Button Content="{Binding Source={StaticResource CustomLocStrings}, Path=LocalizedStrings.ButtonMessage}" />

Now that I have this in place like this I get around the internal modifier issue because I’m actually binding to an instance of my own class (which has a static instance to the Strings resource class).  Using this method I could dynamically change the culture on the fly as well by resetting the culture settings in the thread and resetting the resource.

   1: ComboBoxItem item = ChangeLog.SelectedItem as ComboBoxItem;
   2: Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(item.Content.ToString());
   3: Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(item.Content.ToString());
   4: ((CustomResources)this.Resources["CustomLocStrings"]).LocalizedStrings = new StringLocalization.Resources.Strings();

A bit of a workaround for most scenarios I think.  I think probably remembering to change the modifier may work best for most cases, but this custom instance class might actually fit better into some model implementations.

Summary

Hopefully you can see that for simple string resources the technical implementation is fairly simple.  The real challenge is to you, the developer, to ensure the cultural integrity of the message is being displayed appropriately.  Localization is not an overall easy task and I’m simplifying it to simple strings here.  As I stated above, I applaud those who have successfully implemented fully localized applications.  It can be as simple as a button label or as complex as alternate screen layouts for different cultures!

You can download the code for this post here: StringLocalization.zip

Hope this helps!


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