Tag Archives: WPF

Generating WPF with T4 Templates for Localization

Recently I had the opportunity to explore setting up a small WPF application for globalization/localization. I had no previous experience with this process, so I started by doing some searching and reading. I thought the links below were particularly interesting:

WPF Localization Guidance – Included in this project is The WPF Localization Guidance PDF by Rick Strahl and Michele Leroux Bustamante – I highly recommend reading this! Great details, several approaches are examined.

Creating an Internationalized Wizard in WPF by Josh Smith, Karl Shifflett. Very nice walk thru of building an Internationalized Wizard style app with resource files. This is a very approachable place to start.

WPF Multi-Lingual at Runtime by Andrew Wood – A XmlDataProvider based solution.

Localizing WPF Applications using Locbaml by brunzefb – This link is notable for its comparison of several different approaches.

WPF Localization by Sacha Barber – Interesting because of the third method shown in the article that uses ResourceDictionaries. For more information on using Resource Dictionaries see the answer to this question on Stack Overflow by  Ray Burns.

 

One interesting thing about many of the approaches above is that the focus seems to on localizing strings/text, and to a lesser degree settings, images and other resources. In the context of WPF I was surprised not to find more information about customizing the layout of the UI as part of the translation into another language. I think the structure of XAML encourages a UI composed of many elements that will need more than string substitution to be ideal in another language.

For example, the XAML below and UI it generates seems typical of the kind of composition and ‘richness’ encouraged by WPF/XAML’s structure and tools:

<UserControl
		xml:lang="en"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="basic_styles.xaml" />
                <ResourceDictionary Source="equations.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

	<WrapPanel Style="{StaticResource description_wrap_panel_style}">

		<TextBlock	x:Name="c_textblock"
					Style="{StaticResource description_textblock_style}"
					AutomationProperties.Name = "Enter a Constant, C, that satisfies the following equation: the standard error of the estimate is equal to the constant C over the square root of the sample size">
			Enter a constant, <Italic>C</Italic>, that satisfies
    	<InlineUIContainer Style='{StaticResource image_container_style}'>
    		<Image x:Name='formula_11'
					Source='{StaticResource equation_11}'
					Style='{StaticResource image_style}'>
                <Image.Height>
                    <MultiBinding Converter='{StaticResource image_size}'>
                        <Binding Mode='OneWay'
								ElementName='formula_11'
								Path='Tag'/>
                        <Binding Mode='OneWay'
								ElementName='c_textblock'
								Path='FontSize'/>
                    </MultiBinding>
                </Image.Height>
            </Image>
    	</InlineUIContainer>
	    </TextBlock>

		<TextBlock 	Style="{StaticResource description_textblock_style}"
					KeyboardNavigation.TabIndex="1">
			(<Hyperlink AutomationProperties.Name='More information about the constant C' x:Name='c_hyperlink'>more info</Hyperlink>)
		</TextBlock>

		<TextBox 	Style="{StaticResource entry_textbox_style}"
					AutomationProperties.LabeledBy="{Binding ElementName=c_textblock}"
					KeyboardNavigation.TabIndex="0">
		</TextBox>

	</WrapPanel>
</UserControl>

Certainly changing out all of the strings to translate this example is possible (slightly painful because of the number of string to change out…). In French or Spanish this would work fine:

 

 

However, here is the same UI translated into Japanese – notice that the translator did not keep the elements in the same position.

 

To accommodate a flexible layout in this application I decided to use T4 templates to generate ‘loose’ XAML files. These files are included in the output and parsed at runtime based on the CurrentCulture.

The code below is the contents of the file base_block.tt – this t4 template holds the common elements of the XAML files and will be ‘imported’ by the language specific t4 templates. The language specific templates will provide values for the variables introduced in base_block.tt. One important detail is the use of encoding="Unicode" – I assumed “Utf-8” would work but the XamlParser would error on some characters when the template specified Utf-8, apparently because of the BOM setting…

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".xaml" encoding="Unicode"#>

<UserControl
		xml:lang="<#= this.xml_lang #>"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="basic_styles.xaml" />
                <ResourceDictionary Source="equations.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

	<WrapPanel Style="{StaticResource description_wrap_panel_style}">

		<TextBlock	x:Name="c_textblock"
					Style="{StaticResource description_textblock_style}"
					AutomationProperties.Name = "<#= this.textblock_automation_name #>">
			<#= this.textblock_constant_C_contents #>
	    </TextBlock>

		<TextBlock 	Style="{StaticResource description_textblock_style}"
					KeyboardNavigation.TabIndex="1">
			<#= this.hyperlink_textblock_contents #>
		</TextBlock>

		<TextBox 	Style="{StaticResource entry_textbox_style}"
					AutomationProperties.LabeledBy="{Binding ElementName=c_textblock}"
					KeyboardNavigation.TabIndex="0">
		</TextBox>

	</WrapPanel>
</UserControl>

<#+

	private string xml_lang = @"";
	private string textblock_constant_C_contents = @"";
	private string textblock_automation_name = @"";
	private string hyperlink_textblock_contents = @"";

#>

Note that the variables hold all of the information that I wanted to be able to manipulate – not just the string portions of the UI. The file for English, en.tt:

<# 
	
	xml_lang = @"en";

	textblock_constant_C_contents = 
		@"Enter a constant, <Italic>C</Italic>, that satisfies
    	<InlineUIContainer Style='{StaticResource image_container_style}'>
    		<Image x:Name='formula_11' 
					Source='{StaticResource equation_11}' 
					Style='{StaticResource image_style}'>
                <Image.Height>
                    <MultiBinding Converter='{StaticResource image_size}'>
                        <Binding Mode='OneWay' 
								ElementName='formula_11' 
								Path='Tag'/>                        
                        <Binding Mode='OneWay' 
								ElementName='c_textblock' 
								Path='FontSize'/>
                    </MultiBinding>
                </Image.Height>
            </Image>
    	</InlineUIContainer>";

	textblock_automation_name = @"Enter a Constant, C, that satisfies the following equation: the standard error of the estimate is equal to the constant C over the square root of the sample size";

	hyperlink_textblock_contents = @"(<Hyperlink AutomationProperties.Name='More information about the constant C' x:Name='c_hyperlink'>more info</Hyperlink>)";

#>

<#@ include file="base_block.tt" #>

The file for the Spanish version:

    <#
	
        xml_lang = @"es";

        textblock_constant_C_contents =
            @"Ingrese una constante, <Italic>C</Italic>, para resolver
            <InlineUIContainer Style='{StaticResource image_container_style}'>
                <Image x:Name='formula_11'
                        Source='{StaticResource equation_11}'
                        Style='{StaticResource image_style}'>
                    <Image.Height>
                        <MultiBinding Converter='{StaticResource image_size}'>
                            <Binding Mode='OneWay'
                                    ElementName='formula_11'
                                    Path='Tag'/>
                            <Binding Mode='OneWay'
                                    ElementName='c_textblock'
                                    Path='FontSize'/>
                        </MultiBinding>
                    </Image.Height>
                </Image>
            </InlineUIContainer>";

        textblock_automation_name = @"Ingrese una constante, C, que satisface la siguiente ecuación: el error estándar de la estimación es igual a la constante C sobre la raíz cuadrada del tamaño de la muestra.";

        hyperlink_textblock_contents = @"(<Hyperlink AutomationProperties.Name='Más información acerca de la constante C'>más información</Hyperlink>)";

    #>

    <#@ include file="base_block.tt" #>

Each of the XAML files has a Build Action of ‘Content’ so that it is included in the output for the project.  At runtime I look at the CurrentCulture, compare that to the names of the generated XAML files that are available, feed the file to XamlReader.Load() and add the resulting UserControl into the UI as needed. A small sample app demonstrating this is a available here.

 

I do not have enough experience with globalization/localization to be confident this approach would work in all situations, but for this application it did work and I enjoyed that it: easily allows flexible UI layout, generated XAML files that could be viewed in the VS editor and is simple to use. I seems to me that the biggest downsides to this approach are that it does not allow translators to work on a simple value pair style file and the loose XAML does not allow for a code-behind (which is usually fine but occasionally awkward – at least for me). I would love to hear any comments or feedback on this approach!

 

Special thanks to Lance and Kent for translating for me!! And extra thanks to Lance for pointing out how to cleanly post source code on WordPress.com

 

Enjoy!

CM

Advertisements

Some Experiences in the Display of Mathematical Equations in WPF

I recently needed to display ‘notes’ in a WPF application – the interesting detail was that the notes contained both text and mathematical equations. I did not find a tremendous amount of information about presenting mathematical equations in WPF – so I thought I would share my experiences.

The notes for the application were delivered by the client as a Microsoft Word document.

Because I do not have any experience with Adobe Illustrator or any of the Expression products none of the solutions I investigated involves those products.

 

jsMath – My first idea was to display the notes in a browser control with the hope of reusing them on the web. I quickly found jsMath which generates equations from TeX input (if formulas in TeX are not a familiar idea see Help:Displaying a Formula). Setup was quick and my test page looked nice in Firefox. However, the rendering in the WPF Browser control was unacceptable. It is completely possible that I did not have something configured correctly and/or that there were settings I needed to change, but I decided to move on…

 

MathML – As an alternative to having jsMath handle the equations I briefly looked into using MathML. Unfortunately this page failed to open correctly on my machine in IE8, Chrome, Safari and Opera. That was enough to steer me away from MathML. The page opened without problems in Firefox 3.5…

 

WPF Chromium WebBrowser control – I was not quite ready to give up on the idea of presenting the notes in a browser control with jsMath and thought the rendering in this control might be better. I did not get very far with this and did not try the control in my project – not because of problems with the control, but because the Awesomium project that this control uses is going closed source. I was unsure about the future of the WPF Chromium WebBrowser control without updated versions of Awesomium so I moved on to my next idea.

 

DocumentViewer -  I already had nicely formatted text and equations in the original Word document and thought that I might be able to leverage that. The WPF DocumentViewer control can display XPS documents – and Word documents can be saved as XPS. I saved the first note from the Word document as a .XPS file and – after figuring out that System.Windows.Xps.Packaging is in ReachFramework.dll – quickly put together a test. The file displayed quickly and easily (including equations) and for some applications this could be a great option!  However, I felt the DocumentViewer was too page and print focused for this application. I do not think – at least in it’s default configuration – that the DocumentViewer is a very good solution for displaying shorter items for easy on-screen reading (esp. in a smaller screen area). I am sure I could have tweaked options to improve the DocumentViewer for use in this application, but this seemed like the wrong approach.

 

Word/XPS converted to FlowDocument – I thought these articles were interesting, but was skeptical about the time/results, and did not try this approach – Converting FixedDocument (XPSDocument too) to FlowDocument and WordML to FlowDocument – how to convert docx files to WPF FlowDocument. I would be really curious to hear from anyone how the conversion of Word equations works with the ideas/code presented in these articles.

 

Flow Document  + Images – Images seem like an obvious/traditional choice for presenting the equations and the FlowDocument seemed like an interesting choice for presenting text and images for on-screen reading. One easy way to produce the images is Roger’s Online Equation Editor which transforms LaTeX equations into images. It is easy to work with images in WPF – but the problem I had was getting the images to look as sharp as the text in the FlowDocument when they were resized. If this was a document for printing, or had specific static layout requirements, I think that the images would have worked great. But as the images were resized (either because of dynamic layout changes or changes to the applications design) I was not able to keep them as sharp as the surrounding text. If I had more graphics expertise I may have been able to find a better image type/resolution/rescaling combination.

 

FlowDocument + Shapes – a good friend suggested that I look for ways to represent the equations as SVG. What I eventually found was Inkscape + textext. textext works as an add-in to Inkscape that allows you to render LaTeX. The install and troubleshooting steps near the bottom of the textext page are critical in finding the dependencies (MikTex, Pstoedit, Ghostscript) and dealing with errors (I had to add a directory to my path to get textext working). Once it is up and running textext worked great – it is very easy to create an equation and save it as an SVG file. The next problem is moving the equation from SVG into XAML.

 

SVG to XAML Canvas – All of the methods below can be used to convert an SVG file  into shapes in a XAML Canvas object. In the case of the equations I was producing all of the solutions below generally produced usable output. The Canvas output could be inserted directly into your application – or it could be placed in a resource file by stripping out the Name elements and adding a Key. In addition to using the Canvas directly it could also be used as a VisualBrush.

Xsl – I was able to use the xsl on this page to get canvas based output (and do a some xsl debugging in Visual Studio – a great feature and the first time I have used it).

Inkscape – Inkscape has save as XAML built-in. In the past, for simple images, this worked great for me. For the equations that I was producing in this project the canvas that Inkscape produced apparently lacked a RenderTransform that was crucial to correct display. The problem was fairly easy to fix, but mainly because I had the output from XamlTune as a comparison.

XamlTune – The command line executable in this project worked great – and could potentially be integrated into a build script which is a nice bonus. I had trouble with the gui portion of this project crashing.

The Canvas with shapes output is ok – but I want to reuse the same equation multiple times, use the same equations both as larger stand-alone figures and inline with the text and rescale the larger images as the application’s size changes. I assume that I could get the canvas based resources working for all of those requirements (although I not sure exactly how and imagine a bit of awkward code…). However, I know that having the equations as DrawingImages in a resource file would allow me to use the equations inside an Image tag, solving the problems above.

 

FlowDocument + DrawingImage – The only programs I found that produced DrawingImage output from SVG input were ViewerSvg Professional or Ab2d.ReaderSvg Professional (the ‘Basic’ versions are not listed as having ‘Export as Geometry’ support) from WPF Graphics (demo downloads available – note these will also convert from SVG to a Canvas). This software did nice conversions from SVG to DrawingImage with a good array of options. The Ab2d.ReaderSvg class library also includes a sample application that allows you to combine multiple SVGs into DrawingImages inside a ResourceDictionary, which is almost exactly what I wanted to do! For some situations these applications will not be appropriate since they are commercially licensed software with a cost of $169/$399.

 

Conclusion: The options above present several viable solutions for presenting mathematical equations in a WPF application. Every project will have different requirements that will dictate the best solution. With that said – in my opinion -  the combination of Inkscape and textext to create equations in SVG and Ab2d.ReaderSvg Professional to convert the SVG into DrawingImage resources is the best combination currently available for presenting mathematical  equations for on-screen reading in a WPF application.

 

Enjoy!

CM