Monday, March 23, 2009

Localized WPF Applications

Have you every worried about how you were going to localize your WPF applications?

The WinForms localization model is fairly mature, built into the designer, and utilizes satellite assemblies, that allow for post-build extension of your application. This has many advantages, including being able to farm the localization off to third parties, without requiring them to recompile the original application.

The WinForms model also lead to concise class wrappers and tags for your localized data. That made it easy to access both in the designer, and in the code-behind.

WPF doesn't seem to have the whole localization concept fully baked at the moment. In my opinion LocBaml is to oppresive. Requiring you to spread UID's throughout every XAML document you have, then manually edit every one of them to fit into the planned language.

I just want to localize the words. Can't I use the old satellite assembly approach, for string tables instead of the embedded pages (i.e., BAML resources)?

Yes you can. First decide if you even want the XAML to even be in your satellite resource assemblies. Microsoft says to add the tag into the project file. But, you can localize string tables without doing this.
  • In your project, add a .resx - "Resources File" (I prefer doing this instead of using the existing Settings file - for separation).
       [example: Properties\Resources.resx]
  • Open the resource file, and change the access modifier to "Public".
  • Add additional "Resources Files" at the same level, in the same folder. Inserting the appropriate localization code into the name.
       [example: Properties\Resources.de-DE.resx]
  • Clear the Custom Tool field in the Properties window for each of these files.
  • Open your XAML file and add a reference to the class wrapper namespace.
       [In this case WPFTest.Properties]
  • Then you can go ahead and use elements from the resource string table directly in you XAML
XAML:

<Window x:Class="WPFTest.Window1"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:res="clr-namespace:WPFTest.Properties"

   Title="Window1" SizeToContent="WidthAndHeight">

    <StackPanel>

        <Button Name="button1" Click="button1_Click">Button</Button>

        <Label Name="label1" Content="{x:Static res:Resources.TestString}" />

    </StackPanel>

</Window>

CS Code:

        private void button1_Click(object sender, RoutedEventArgs e)

        {

            Properties.Resources.Culture = new System.Globalization.CultureInfo("de-DE");

            Window1 wnd = new Window1();

            wnd.Show();

        }

Note: You need to set the culture for the resources (if you wish to override the current system settings), before you show the WPF window.

Sunday, March 22, 2009

SQLite A Good Alternative

In the past I have talked about SQL Server Desktop Edition and VistaDB..

I've really been searching for that perfect - elusive, small, embedded, free database. Something that doesn't need an installation, supports encryption and just works. Especially in C#....

.Net is capable of xcopy deployment, and it would be nice if my database could go with me..

I have been leaning toward SQL CE (especially 3.5). It meets most of my requirements (you can copy the dll's for private deployment - and don't need to install)..

But, I think I found a better candidate: SQLite!.

And it is already inside of some things you are probably already using:
  • Adobe (Photoshop Lightroom, Air, possibly Acrobat Reader)
  • Apple (Apple Mail, Safari, possibly iPhone, and iPod touch)
  • Firefox
  • Google (Google Gears, Android)
  • McAfee
  • Microsoft (maybe in a game?)
  • Philips (mp3 players)
  • PHP
  • Python
  • REALbasic
  • Skype
  • Sun (Solaris 10)
  • Symbian
  • Toshiba

I have been playing with it for a while in Visual Studio 2008, and am very happy with what I am seeing. The ADO.Net provider gives very clean integration. I am having no problem accessing my data either through the Server Explorer, Direct Connections, LINQ, or even ADO.Net Entity Framework. It is fast, small, and has encryption support (the ADO.NET provider version -link provided below- includes 128 RC4 encryption).

Of course there are a few things to work around. SQLite itself is written in C, and the ADO.NET provider is a managed wrapper around the compiled code. So although .Net can target AnyCPU, there are flavors of the managed wrapper for x86, x64, Itanium, and ARM. Fortunately .Net seems to utilize late binding, and you can figure out your processor architecture and copy the correct DLL into place in time for your app to run properly.

SQLite Home Page
System.Data.SQLite ADO.NET Provider
SQLite Admin - UI Management Console

An example of copying the correct DLL in place:

    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            try
            {
                FileInfo fi = new FileInfo(Assembly.GetEntryAssembly().Location);
                string strSource, strDest = fi.DirectoryName + "\\System.Data.SQLite.dll";
                if (Microsoft.Build.Utilities.ProcessorArchitecture.CurrentProcessArchitecture == Microsoft.Build.Utilities.ProcessorArchitecture.AMD64)
                {
                    strSource = fi.DirectoryName + "\\System.Data.SQLitex64.dll";
                }
                else
                {
                    strSource = fi.DirectoryName + "\\System.Data.SQLitex86.dll";
                }
                // Copy the correct dll in place
                if (!File.Exists(strDest)  new FileInfo(strSource).Length != new FileInfo(strDest).Length)
                {
                    File.Copy(strSource, strDest, true);
                }

            }
            catch (Exception exc)
            {
                MessageBox.Show(string.Format("Error copying DLL: {0}",exc.Message));
            }
        }
    }