Minggu, 23 Januari 2011

Silverlight 4 + RIA Services - Ready for Business: Localizing Business Application


To continue our series, let’s look at localizing our business applications. In today’s global village, it is often no longer OK to support only one language. Many real world business applications need to support multiple languages. To demonstrate the pattern, let’s look at localizing the Silverlight Business Application Template.

Update (3/28): I added a bit more explanation about setting culture in Visual Studio..

You can download the completed solution.

Here it is in English side-by-side with a localized version (notice the Hebrew is rendered Right-To-Left correctly):

image

image

Let’s start by creating a new Silverlight Business Application. Notice there is a server and a client project in the solution. The strings that are needed in both the server and client project are located in the server project and those that are only ever needed in the Silverlight client are in the client project. Let’s start with the server.

image

In the Resources folder we see two sets of resources. One for the Error messages and another for registration details. These are in a the ResX format which is an Xml file format that has been around for a long time.. there are many localization tools outside of VS that support this format. Here I will show the localization in VS.

One setup step before we get started. You need to explicitly tell Visual Studio what languages are supported in by the Silverlight application. Unfortunately, this is not supported by the VS Project Properties dialog, so you need to edit the Silverlight Applications csproj file directly.

Right click on the Silverlight project and unload

image

Right click again and select “Edit MyApp.csproj

image

Then find the SupportedCultures attribute and add the full list of cultures your application supports.

image

Then right click on the project and re-load project.

Ok, done with the setup, on to the actual localization.

The first step is to create a copy of each of these files with the [Local].resx extension. For example, I created one called “ErrorResources.he.resx”. Here is a full list of cultures codes.

Then open the file in VS and you see a nice editing experience. Simply replace the English text with the translated version.

Below is the translation experience in progress:

image

Repeat for the RegistrationDataResources.resx file.

Next we need to make these new *.he.resx accessible from the client. First let’s find the client resources directory. Notice the English resources are already linked in here. We need to add the Hebrew ones we just created.

image

Right click and add an Existing Item

image

Then browser to the ErrorResources.he.resx file in the web project.

image

Then select add as Link

image

Repeat for the other localized files from on the client.

image

In ApplicationStrings.resx and ApplicationStrings.he.resx I added one additional value to indicate flow direction

image

I then made two very small tweaks to the template to use this value. In each top level window, I need to set the flow direction.

in Main.xaml:

<UserControl
  x:Class="LocalizedBusinessApplication.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  FlowDirection="{Binding Path=ApplicationStrings.FlowDirection, Source={StaticResource ResourceWrapper}}" >

and in View\LoginRegistrationWindow.xaml

<controls:ChildWindow
  x:Class="LocalizedBusinessApplication.LoginUI.LoginRegistrationWindow"
  x:Name="childWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  Width="400"
  Title="{Binding Path=ApplicationStrings.LoginWindowTitle, Source={StaticResource ResourceWrapper}}"
  Style="{StaticResource LogRegWindowStyle}"
  FlowDirection="{Binding Path=ApplicationStrings.FlowDirection, Source={StaticResource ResourceWrapper}}"
  Closing="LoginWindow_Closing">




Now, we are ready to run the app.



image



It looks great, but it is all English.. how can we test the localization? Simply change your language in the browser.



In IE that is Tools\Internet Options



image



Make sure Hebrew is first. Then hit refresh on the page and…



image





What I showed here was how to localize a Silverlight application using the Business Application Template.





Enjoy!

A Great Time at Developer Academy 4 In Israel



I had a great time today at Developer Academy 4. It was in a beautiful part of Tel Aviv. The weather was great and the food fantastic. They had well over 1,000 paid attendees, so the house was PACKED. Dev Academy’s page on Facebook. Including some pictures of me.


I was very impressed with the high tech community in Israel. Clearly Israel is a hub of hightech.


I did a demo in the keynote where I showed off a few of the cool new features in Silverlight 4.


image


Update:


You can watch the Israel Developer Academy keynote and my breakout session on RIA Services:


Then I demoed the Silverlight PhotoBooth app to show Webcam support and raw video\image editing capabilities.


image


Then I showed the HtmlPuzzle application to show off out of browser support and deep integration with HTML as well has flash video


image


Then I demoed the Silverlight text editor, showing off rich text experiences, printing, right click, etc.


image


Finally, i ended with showing the Silverlight business application localized to Hebrew with RTL support and some basic RIA Services demo. See my blog post for more information: Silverlight 4 + RIA Services - Ready for Business: Localizing Business Application


image_thumb7


I had a great time at Dev Academy 4 and I am looking forward to seeing more of Israel!

Silverlight 4 + RIA Services - Ready for Business: Validating Data


To continue our series let’s look at data validation our business applications. Updating data is great, but when you enable data update you often need to check the data to ensure it is valid. RIA Services as clean, prescriptive pattern for handling this. First let’s look at what you get for free. The value for any field entered has to be valid for the range of that data type. For example, you never need to write code to ensure someone didn’t type is “forty-two” into a textbox bound to an int field. You also get nice looking and well behaved validation exposure in the UI.

image_thumb[82]

Note: if you are not seeing this, ensure that “ValidatesOnExceptions=True” is in the binding expression for the each field

Of course, that sort of validation only goes so far, in real application you need some more extensive validation. And for this validation you absolutely have to check the validation before your business logic is run because you don’t know what client might be sending you the update, in addition, you want to check the validation on the client to give the user a really nice user experience and reduce the number of error-case hits to your server which reduces server load. In traditional models, you need do error checking twice to cover both of these cases. But that is obviously error prone and easy to get out of sync. So RIA Services offers a common model for validation.

The most common cases are covered by a set of custom attributes you apply to your model on the server. These attributes are common across the .NET Framework supported by ASP.NET Dynamic Data, ASP.NET MVC and RIA Services. You can find the full set in System.ComponentModel.DataAnnotations. But to give you a flavor:

[Display(Order = 0)]
[Required(ErrorMessage = "Please provide a name")]
public string Name { get; set; }
[Range(0, 999)]
public Nullable<decimal> Price { get; set; }
[RegularExpression(@"^http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?$",
                    ErrorMessage = "Please use standard Url format")]
public string Uri { get; set; }




image_thumb[84]



As you an see from above the validations on the client are handled automatically, but they are also run again on the server BEFORE your Update method is called. This allows you to keep the validation gunk out of your business logic for Update. The other great thing is that these validations will apply in exactly the same way no mater where that entity is used in the UI because they are build into the model. You can of course localize the error messages by passing a resource ID rather than a hard code string. You can also read these validation metadata out of an external config file or database rather than using attributes in the code.



But clearly that doesn’t cover all cases. Many times you need to write some actual procedural code. Let’s consider an example of validating the description to ensure it is really complete. There is no custom attribute for that ;-). So let’s write a bit of C# code. First we need to indicate that the Description field has some custom validation.



[CustomValidation(typeof(PlateValidationRules),
                  "IsDescriptionValid")]
public string Description { get; set; }


Then you can easily create this class and implement the IsDescriptionValid method.





  1:         public static ValidationResult IsDescriptionValid(string description)
  2:         {
  3:             if (description != null && description.Split().Length < 5)
  4:             {
  5:                 var vr = new ValidationResult("Valid descriptions must have 5 or more words.",
  6:                     new string[] { "Description" });
  7:                 return vr;
  8:             }
  9:
 10:             return ValidationResult.Success;
 11:         }
 12: 




Notice in line 1, the signature of the method must return a ValidationResult – this is a class from DataAnnotations that contains information about any validation errors. The method also has to take one parameter that is the same type of the field it is being applied to. You could do it on the entity level to do cross field validation.



Next on line 3, i am implementing some very lame algorithm for determining if the description is valid or not.



On line 5 and 6, I return an error and indicate which field this applies to.



Now, run the application. You will see that we can edit the description and tab off with no error, but if we submit, then we get back an error in exactly the same way as we saw before. Notice I could have sent several entities and each of them could have errors. RIA Services keeps up with each of them (we even give you a list) and as the user edits each one we show some UI like this.



image_thumb[86]





Note, if you see this sort of dialog instead:



image_thumb[85]



It likely means you need to write a handler for SubmitChanges on your DomainDataSource.





<riaControls:DomainDataSource  SubmittedChanges="plateDomainDataSource_SubmittedChanges"




        private void plateDomainDataSource_SubmittedChanges(object sender, SubmittedChangesEventArgs e)
        {
            if (e.HasError &&
                e.EntitiesInError.All(t => t.HasValidationErrors))
            {
                e.MarkErrorAsHandled();
            }
            else
            {
                System.Windows.MessageBox.Show(e.Error.ToString(), "Load Error", System.Windows.MessageBoxButton.OK);
                e.MarkErrorAsHandled();
            }
        }


Now, this is very cool because we have the full power of .NET to write our validation rules. But the down side is that I only get validation once the change is submitted. What I’d really like to do in some cases, is have some write some custom validation logic and have it execute on the server AND the client. Luckily we have the power of .NET on the client and the server so we can use shared code validation. To enable this simply change the name of the file that contains the validation rule to include “.shared.cs” post-fix. This changes causes the RIA Services MSBuild logic to compile the file for the client and the server.



image_thumb[88]





Now the exact same code will run on the client and the server. So if there is a bad description, we no longer have to round-trip to the server to work that out.



image_thumb[90]



Of course, in a real world case you are likely to have both server-only and shared validation rules and RIA Services fully supports that scenario as well. Simply define any shared validation rules in .shared.cs files and any server-only validation rules in another file.

Program Manager (PM) Tip Index


I thought it would be worthwhile to produce an index for the (very) occasional series I have on giving tips to PMs at Microsoft.


PM Tip #10: Interview Tips for Landing a Great Program Management Job


PM tip# 11: Information is the Currency of Program Management


PM Tip #14: Great teams have members that defy roles


PM Tip #12: Don't waste keystrokes


PM Tip #21: Sanity Will Prevail


PM Tip # 32: Nothing kills excitement like ambiguity


PM Tip #57: Write the agenda on the board


PM Tip #72 Instant feedback...


PM Tip #73: The 'Be More Visible' Sham


PM Tip #78: Reveal your stupidity


Presentation tip: They Remember the Mistakes more than the Polish


Building Consensus


That is a full day's work!


The What and the How


Thoughts on writing reviews...


The Wisdom of Crowds: Rethinking consensus



Maybe one day I will do the other 89 tips ;-)

Great Presentation to the Israel Dot Net Developers User Group (IDNDUG)


I continue to be impressed with the quality of .NET Developers in Israel.. we had a full house last night for a 2+ hour presentation on building business applications with Silverlight and RIA Services. The audience was very engaged and had lots of good, relevant questions which created a really good conversation. Check out the slides and demo.

I started off by demoing the Right-to-Left text support for Hebrew that is baked in as part of Silverlight 4.

image

Then I started from scratch, with File\New Project and built out a simple data-oriented application.. basically I walked though most of this series.

You can find the completed demo, including the files and code snippets you would need to repeat the demo yourself, which i would be high flattered if you did at your own user’s group or for your development team. You an find the demo steps in the series.

image

A couple of interesting links:

- We talked about the ViewModel (MvvM) support for RIA Services. I high recommend checking out MIX10 Talk - Slides and Code and View/ViewModel Interaction - Bindings, Commands and Triggers

- We also talked about SEO – here is a good post on it: Silverlight 4 + RIA Services - Ready for Business: Search Engine Optimization (SEO)

- More details on the Silverlight out of browser work check out Tim Heuer’s video Out-of-browser Experiences

- During the break I showed a world map showing hits to my blog in real time from all over the world. This is Stumpler.. it is a VirtualEarth, Silverlight, Azure based service from Brian Hitney. You can add your own blog for free or just watch the hits to mine. I expect to see Israel lighting up more often now ;-)

Thanks to Jackie Goldstein and Guy Burstein for organizing the event and for Jackie and Kim for the unique tour of a Kibbutz – it was great fun!

RIA Services and Validation


BookClub Application ScreenshotEarlier today, my SilverlightTV recording on RIA Services and Validation went online. I used validation as a feature area to focus on this first recording on RIA Services, because I think it illustrates both the RIA Services value proposition and key elements of the vision around the project in a very direct manner. Specifically:




  • Focus on end-to-end solutions for data scenarios. It is not sufficient to just address querying data or submitting some changes, but about providing the infrastructure for managing data, editing with validation, tracking errors raised on client and/or server, rolling back changes, and more.

  • The server project and client project are logical halves of the same application. It would be great to preserve and propagate developer intent, and semantics from the database to the middle tier to the client, and share code across tiers where possible. In other words leverage intent and rules as close to the end-user, while enforcing them at each tier. Automatically.

  • Provide out-of-box solutions to common scenarios. The Entity class on the client provides a pretty complete implementation of working with data on the client. It provides identity management, change tracking, change notification, transacted editing and rollback capabilities, as well as tracking and surfacing validation and concurrency conflict errors. In particular, in the context of this post, Entity provides an implementation of infrastructure interfaces such as INotifyDataErrorInfo introduced in Silverlight 4, so you don't have to roll your own.



In my demo for SilverlightTV, I used a product catalog and product editing scenario (my favorite scenario if you ask folks on the team). However for MIX10, I used a Book Club scenario, which I think is quite a bit more interesting (with the potential to become my new favorite scenario).


Check out the video. And then come back to read further. My MIX10 summary post lists everything else going on in the Book Club sample (along with a link to all of the sample code), but the rest of the post focuses on the validation aspects.


Update 3/26/10: For those looking for a copy of the app demo'd on the SilverlightTV show, you can download the Store sample.



[Full post continued here...]

Silverlight 4 + RIA Services - Ready for Business: Authentication and Personalization


To continue our series, In real business applications our data is often very valuable and as such we need to know who is accessing what data and control certain data access to only users with privilege. Luckily this is very easy to do with RIA Services. For example, say we want to let only authenticated users access our data in this example. That is as easy to accomplish as adding an attribute, see line 2 below.

  1:     [EnableClientAccess]
  2:     [RequiresAuthentication]
  3:     public class DishViewDomainService : LinqToEntitiesDomainService<DishViewEntities>
  4:     {
  5: 




When we run the application, we now get an error. Clearly you can do a bit better from a user experience angle… but the message is clear enough.



image_thumb[93]





Notice there is a login option, so we can log in…



image_thumb[107]





and even create a new user.



image_thumb[108]





and with a refresh we now get our data



image_thumb[97]



And the application knows who i am on the client and gives me a way to log out.



Now you can also easily interact with the current user on the server. So for example, only return records that they have edited, or, in this case, log every access:





  1:         public IQueryable<Restaurant> GetRestaurants()
  2:         {
  3:             File.AppendAllLines(@"C:\Users\brada\Desktop\log.txt", new string[] {
  4:                 String.Format("{0}:{1}", DateTime.Now,
  5:                 this.ServiceContext.User.Identity.Name)});
  6:             return this.ObjectContext.Restaurants
  7:                 .Where (r=>r.Region != "NC")
  8:                 .OrderBy(r=>r.ID);
  9:         }
 10: 




Line 5 is the key one.. we are accessing the current users on the server. This gives us a nice simple log.



3/7/2010 9:42:57 PM:darb

3/7/2010 9:43:05 PM:darb



Now we can also personalize this a bit. Say we want our users to be able to give us a favorite color and we keep track of that on the server and the client, so it works seamlessly from any machine.



First we need to add BackgroundColor to our backing store. I this case I am using ASP.NET profile storage, so I add the right stuff to web.config



image_thumb[103]



Then I need to access this from the Silverlight client, so I add a property to the User instance in the Models\User.cs



    public partial class User : UserBase
    {
        public string FriendlyName { get; set; }
        public string BackgroundColor { get; set; }
    }


Finally, we need to access it on the client. In main.xaml add lines 2 and 3..



  1:   <Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}"
  2:         Background="{Binding Path=User.BackgroundColor}"
  3:         DataContext="{StaticResource WebContext}">
  4:
  5: 




Run it and we get our great default background color!



image_thumb[104]



Now, that is nice, but it would be even better to give the user a chance to actually edit their settings. So in About.xaml, we use a very similar model as above.



  <Grid x:Name="LayoutRoot"
        DataContext="{StaticResource WebContext}">


and



<sdk:Label Content="Background Color:" />
<TextBox Text="{Binding Path=User.BackgroundColor, Mode=TwoWay}" Height="23" />


Then wire up a save button



        private void button1_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            WebContext.Current.Authentication.SaveUser(false);
        }




And it works!



image_thumb[105]



And what’s better is if you run it from another browser, on another machine, once you log in you get the exact same preferences!



image_thumb[106]