SharpDevelop Community

Get your problems solved!
Welcome to SharpDevelop Community Sign in | Join | Help
in Search

Daniel Grunwald

Creating a localizable WPF dialog

This post explains how to create a localizable dialog with WPF using the SharpDevelop infrastructure, i.e. when writing a SharpDevelop AddIn. If you are writing a standalone application, you can still get ideas from the post, but you will have to define the referenced styles and markup extensions yourself.

If we create a simple dialog using the designer, the XAML code might look like this:

<?xml version="1.0" encoding="utf-8"?>
<Window
    x:Class="SomeNamespace.MyDialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="125" Width="300"
    Title="This is my dialog">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBox
            Grid.Column="0" Grid.Row="0"
            Margin="8,8,8,8">
        This big text box is representing the main content.
        </TextBox>
        <StackPanel
            Orientation="Horizontal"
            Grid.Column="0" Grid.Row="1"
            HorizontalAlignment="Right" VerticalAlignment="Bottom"
            Margin="0,0,8,4.25"
            Width="160" Height="24.5">
            <Button
                Content="OK"
                IsDefault="True"
                Width="75" Height="23" />
            <Button
                Content="Cancel"
                IsCancel="True"
                Margin="4,0"
                Width="75" Height="23" />
        </StackPanel>
    </Grid>
</Window>

This results in the following window:

Now, there is a whole bunch of issues with this dialog. The most obvious are that the text on the buttons is blurry, and that the background color is wrong (dialogs are expected to use the Control color, a light grey). Also, this window is shown in the task bar - dialogs shouldn't do that. We can fix all of these issues by applying a style to the window:

<Window ... xmlns:core="http://icsharpcode.net/sharpdevelop/core" Style="{x:Static core:GlobalStyles.DialogWindowStyle}">

This style will do the following things:

  • Use 'Control' as background color
  • Hides the window from the task bar
  • Enable WPF 4 layout and text rendering (makes everything less blurry)
  • Enables right-to-left layout if the current language is a right-to-left language

If you have a normal window (not dialog), then you can also use {x:Static core:GlobalStyles.WindowStyle}, which does not set the background color and does not hide the window in the task bar.

Now let's get to the localization part: we need to load the translated string resources. The {core:Localize} markup extension can be used to do this (assuming the string resources are registered with the SharpDevelop ResourceService):

<Window ... Title="{core:Localize SomeNamespace.MyDialog.Title}">
...
<Button
Content="{core:Localize Global.OKButtonText}" .../>

There are a few global resources for commonly used elements, but all other strings should be added using keys that indicate where the resource is being used.

But now we're running into more potential issues: what if the translated string doesn't fit on our button? All explicitly specified widths are potential trouble-makers, so let's get rid of them. Let's trust the automatic layout being done by WPF.

The title is missing because we forgot to define that string in the resource file. But let's ignore that issue and concentrate on the buttons instead.

'OK' is way too small, and even 'Abbrechen' (German for Cancel) could use some more spacing between the button border and the text. Fortunately, SharpDevelop again provides a style for this common issue. We could apply that style to each button individually, but let's register it in our window so that it automatically gets picked up by all buttons:

    <Window.Resources>
        <Style TargetType="Button" BasedOn="{x:Static core:GlobalStyles.ButtonStyle}"/>
    </Window.Resources>

This style will give the button the correct padding (9,1) and minimum width (73).

These buttons look pretty reasonable - but if you look closely, you'll notice that the "Abbrechen" button is a bit wider than the "OK" button (80 pixels vs. 73 pixels). In this case, it's not a big problem; but the effect might be more pronounced in other languages, or with other text on the buttons; so let's take a look at how to fix this. WPF has the container "UniformGrid" to assign the same size to a set of controls. However, if you try to apply that in this case, you'll notice that the UniformGrid will include the margin in the size calculation, so whichever button has the margin set will appear to be a bit smaller.

There are two solutions to this problem: either evenly distribute the margin over both buttons (give OK a right-margin of 2; and Cancel a left-margin of 2), or use the UniformGridWithSpacing container. Here, we use the latter approach, which has the advantage that it can be extended to more than 2 buttons without having to think about the distribution of margins.

UniformGridWithSpacing is defined in ICSharpCode.SharpDevelop.Widgets, so we'll need to import that namespace: xmlns:widgets="http://icsharpcode.net/sharpdevelop/widgets"

Here's how you use the grid:

        <widgets:UniformGridWithSpacing Columns="2"
            Grid.Column="0" Grid.Row="1"
            HorizontalAlignment="Right"
            Margin="0,0,12,12">
            <Button Content="{core:Localize Global.OKButtonText}" IsDefault="True" />
            <Button Content="{core:Localize Global.CancelButtonText}" IsCancel="True" />
        </widgets:UniformGridWithSpacing>

The spacing can be defined using the SpaceBetweenColumns property, but that's not necessary in this case as the default value (7) is correct for this purpose. And yes, the Windows User Experience Interaction Guidelines really suggest 7 pixels here; not 4 or 8 as is often mistakenly assumed (I made the same mistake in the Window we started with).

Finally, you should ensure that your dialog doesn't show a common bug: open your dialog, then switch to another application, then switch back to SharpDevelop. What should happen is that your dialog appears, forcing the user to finish whatever he was doing with your dialog. If the dialog does not appear and the SharpDevelop main window is unresponsive, you forget to give your dialog an owner. In the code creating the window (ideally just before calling w.ShowDialog()), add:

w.Owner = WorkbenchSingleton.MainWindow;

If your dialog is triggered by another dialog, then use your immediate parent window as owner instead.

Published Nov 05 2010, 04:06 PM by DanielGrunwald
Filed under:

Comments

No Comments
Powered by Community Server (Commercial Edition), by Telligent Systems
Don't contact us via this (fleischfalle@alphasierrapapa.com) email address.