h3mm3's blog

Stories from behind the keyboard

  • RSS
  • Twitter
Showing posts with label WPF. Show all posts
Showing posts with label WPF. Show all posts

One straightforward way to localize a Windows Phone 7 application you are writing is the following:

  • Create and edit a .resx file (for instance LocalizedStrings.resx).
  • Build your project, so that Visual Studio (well, the PublicResXFileCodeGenerator tool) generates the .Designer.cs file. This way your assembly is having a nice LocalizedStrings static class that you can use in your XAML.
  • Register the static class as an application resource, in your App.xaml:
<Application ... 
 xmlns:my="clr-namespace:{your default namespace}">
<!-- .... -->

<Application.Resources>
  <!-- .... -->
  <my:LocalizedStrings x:Key="Loc"/>
</Application.Resources>
  • Feel free to bind to any localized string in your XAML using the built-in Managed Resource Editor. For instance, if you have a string resource called MyLocalizedString, image you can write somewhere in your XAML’s:
<!-- .... -->

<TextBox Text="{Binding MyLocalizedString, Source={StaticResource Loc}}" ../>

<!-- .... -->

At this point you build and run your project….

image

...and sudden notice that your program gives a creepy "AG_E_PARSER_UNKNOWN_TYPE" error. (“WTF!?”): image

This error says that the runtime parser doesn't know a type, but stays absolutely silent about the type’s name! The unknown type is just the LocalizedStrings class and it is unknown to the parser because the PublicResXFileCodeGenerator tool made its constructor internal.

image

Editing by-hand the code behind is pointless, since every time you edit or add a resource in the Managed Resource Designer, the PublicResXFileCodeGenerator tool rebuilds the LocalizedStrings.Designer.cs and (re-)sets the constructor to “internal”.

At this point you can take one of the following steps:

  • Commit to Android development
  • Change your mind about binding to resources
  • Use an IL weaving technique and make the constructor’s access modifier public
  • Follow KISS and YAGNI principles (kind of..) and just replace “internal” with “public” before building your project.

The latter is the path I followed, after a little struggle with NuGet (I couldn’t find any proper NuGet Fody add-on, precisely).

To put it shortly, I wrote a tiny REPLACE.EXE console utility. It takes 3 input parameters: a full filename, a text to find and a text to replace. Then I set the following pre-build action for my WP7 application project - $(ProjectDir) is a Visual Studio macro; you can find it and more on  MSDN’s “Pre-build Event/Post-build Event Command Line Dialog Box” page).

image

image

This way, anytime I build my project, REPLACE.EXE resets the LocalizedStrings contructor to public.

All’s well what ends well; happy programming!

P.S: Here’s the RESET.EXE’s Main()…

image

If you want to add a simple type value value-type value to a ResourceDictionary or to the Resources property of a WPF/Silverlight control (or whatever)… you simply can’t. I tried writing something like the following code, but suddenly I had to admit there’s no way to set a value… and this is almost acceptable, since a simple type doesn’t have a property to set!

<System:Int32 x:Key="MyIntegerResource" NowWhat??/> 

In order to store something in your resource file or property, you have to store it as an object. So, as far as simple types are concerned. you have to wrap them into an utility class. The following is my utility Wrapper code definition:

public class Wrapper 
{ 
  public object Value { get; set; } 
}
So far so good… now I can store a simple value using such a XAML snippet:
<my:Wrapper x:Key="aSimpleValue" Value="123"/>

Wait… this isn’t what I expected… Value will be the String “123”, not the Int32 number 123. Moreover, this wrapper doesn’t look to me like a complete solution.

My next step is creating (yet another) MarkupExtension. As I told in my previous post, you can extend XAML creating a markup extension, which you will use like you do with {Binding}, {StaticResource} and so on… The extension I’m creating will let me instanciate any type, provided there is an IConverter from String to the type I need. The following code describe how I intend to use this extension:

<!-- using it as a wrapped resource -->
<my:Wrapper x:Key="aDateValue" 
Value="{my:InstanceOf Type=core:DateTime, Value='1/1/2010'}"/> 

<!-- using it in a standard property, redundant enough! -->
<TextBlock Width="{my:InstanceOf Type=core:Double, Value='12'}"/>

All in all, I have to declare a class called InstanceOfExtension, derive it from MarkupExtension, declare a Type property called Type, a String property called Value and override the method ProvideValue(). Here comes the code:

public class InstanceOfExtension:MarkupExtension 
{ 
  public Type Type { get; set; } 
  public String Value { get; set; } 
  public override object ProvideValue(
  IServiceProvider serviceProvider) 
  { 
    try 
    { 
      return Convert.ChangeType(Value, Type); 
    } 
    catch 
    { 
      return null;   
    } 
  }
}

The method Convert.ChangeType(value, type) invokes the implementation of the conversion method defined by the IConvertible interface of the String Value (e.g Value.ToInt16(), Value.ToSByte() and so on, read MSDN guide for the complete list).

Conclusion

The technique used here can be fine tuned. For instance, converting only to simple types is quite reductive if not futile, since you can simply use the standard conversion capabilities of XAML language. Anyway the extension described here gives you full control over the converted type and fits perfectly to the Wrapper class needs.

In this post I’m showing how to create a simple XAML markup extension. A markup extension is a class derived from MarkupExtension, and it can be used to provide further extensibility to the XAML markup code. The custom markup extension showed here (called AutoTagExtension) will provide auto-numbering capability to any element (class) or attribute (property) of the markup code. Moreover, this markup extension is likely to be useless in a LOB scenario.

Any instance of the class AutoTagExtension can be configured with a Prefix (a custom string) and will produce a formatted string like “<Prefix><Count>,” where <Prefix> is the prefix set in the XAML by the designer, and <Count> is an Integer automatically generated based on the number of elements (having the same Prefix) already present in the markup. Since this markup extension will return a string, I’ll use this extension in standard properties such as Run.Text and Button.Content. For instance:

<Button Content="{AutoTag Prefix=AButton}"/>


The markup extension



The class AutoTagExtension is declared as follows (note that the suffix –Extension can be stripped out when using the class in the XAML code)



using System;
using System.Collections.Generic;
using System.Windows.Markup;

namespace WpfMarkupExtentionsDemo
{
public class AutoTagExtension:MarkupExtension
{
//Default constructor: enables writing {AutoTag} in the markup
public AutoTagExtension()
{

}

//Parameterized constructor: enables writing {AutoTag myPrefix} in the markup
public AutoTagExtension(string prefix)
{
Prefix = prefix;
}

//Use {AutoTag myPrefix,true} to reset the count of “myPrefix” elements
public AutoTagExtension(string prefix, bool resetCount)
{
Prefix = prefix;
if (resetCount) ResetCount(prefix);
}

//Public instance property: enables writing {AutoTag Prefix=myPfx} in the markup
public string Prefix { get; set; }

//This one makes the magic happen
public override object ProvideValue(IServiceProvider s)
{
return String.Format("{0}{1}", Prefix, GetCount(Prefix));
}

#region Static members provide counting capability
private static readonly Dictionary<string,int> _counts 
= new Dictionary<string, int>();

//C# doesn’t support static indexers, so I write a “classic” getter function...
protected static int GetCount(string prefix)
{
if (_counts.ContainsKey(prefix))
{
return ++_counts[prefix];
} else
{
_counts.Add(prefix, 0);
return 0;
}
}
//This one enables resetting the counter
protected void ResetCount(string prefix)
{
_counts.Remove(prefix);
}

#endregion
}
}


Using my custom markup extension in the XAML code



The usage of the extension in the XAML code is straightforward: simply declare a namespace alias for the library containing the markup extension (e.g. “my”) and use it as follows:.



<StackPanel>
<Button Content="{my:AutoTag Button,true}"/>
<Button Content="{my:AutoTag Prefix=Button }"/>
<TextBlock>
<Run FontStyle="Italic" Text="{my:AutoTag 'Run '}"/>,
<Run Text="{my:AutoTag 'Run '}"/>
</TextBlock>
<Button Content="{my:AutoTag Button, true}"/>
</StackPanel>


The first usage invokes the constructor that accepts two input parameters: the prefix and the flag resetCount. The second usage invokes the default constructor and set a value for the Prefix public property. Subsequent usages are similar.



Design-time behavior



At design-time the markup extension is already alive and kickin’, as you can see in this screenshot:



markext01



If you close and re-open the designer, the static counts stay alive. As a consequence, at design-time you’ll see that the two Run elements are counted 2 and 3 (and not 0 and 1 as when opening the designer the first time):



markext02



Conclusions



Custom MarkupExtensions represent one of the many extensibility points given by WPF and XAML. In this simple sample I showed the basics about declaring and using a custom markup extension and how to provide transversal capabilities to the XAML elements, thanks to the usage of static members. You can easily find similar usages for any real LOB scenario.

If you want to encapsulate some behavior into a class in order to reuse it by writing simple XAML code, you can inherit from a bunch of classes such as Behavor, TriggerActions,  and TargetedTriggerAction. For instance, in the following code I created a MagnifyTriggerAction, which provides an object the capability to be scaled when an event occours.

public class MagnifyTriggerAction:TriggerAction<frameworkelement>
{
 public double Factor { get; set; }
 protected override void Invoke(object parameter)
 {
  var zoom = new ScaleTransform
   {
     ScaleX = Factor,
     ScaleY = Factor,
     CenterX = _centerX,
     CenterY = _centerY
   };
  AssociatedObject.RenderTransform = zoom;
 }

 protected double _centerX, _centerY;
 protected override void OnAttached()
 {
   base.OnAttached();

   _centerX = .5 * AssociatedObject.Width;
   _centerY = .5 * AssociatedObject.Height;
 }
}

The following code shows a somewhat more complex TriggerAction, inherited from the previous class. This class inherits from its base class the behavior of attaching a ScaleTransform to the AssociatedObject and has the new capability of altering the Opacity of the AssociatedObject based on the value of two properties (for the sake of simplicity, here I haven't implemented them as DependencyProperties but of course you can do it...).

public class MagnifyAndFocus:MagnifyTriggerAction
{
 public double OpacityAfterTrigger { get; set; }
 public double OpacityBeforeTrigger { get; set; }

 protected override void Invoke(object parameter)
 {
  base.Invoke(parameter);
  if (OpacityAfterTrigger!=0)
   AssociatedObject.Opacity = OpacityAfterTrigger;
 }
 protected override void OnAttached()
 {
  base.OnAttached();
  if (OpacityBeforeTrigger!=0)
   AssociatedObject.Opacity = OpacityBeforeTrigger;
 }
}

Using these TriggerActions is quite simple. For instance, first of all I declared the System.Windows.Interactivity namespace and assembly (and thanked Blend 3 for it).
<UserControl
 ...
 xmlns:b="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
 ...
>

Then, in the XAML, I attached the TriggerAction to an object (here an hypotetical SomeElement type) using an Interaction.Triggers element that wraps two EventTriggers. When the first TriggerAction is attached to the object (by the first EventTrigger), it sets the Opacity of the object to the OpacityBeforeTrigger value (in this case, it's 0.5) - see the OnAttached(..) implementation. The first trigger will fire on every MouseEnter event and start the Invoke(..) method of the MagnifyAndFocus TriggerAction, wich, in sort, zooms the object a little bit (120%) and restores its Opacity to 1.0. The second trigger will reset scaling and opacity, on every MouseLeave event.

<SomeElement Rows="3" Columns="3" Width="50" Height="50">
 <b:Interaction.Triggers>
  <b:EventTrigger EventName="MouseEnter">
   <my:MagnifyAndFocus 
    Factor="1.2" 
    OpacityAfterTrigger="1.0"
    OpacityBeforeTrigger="0.5"/>
   </b:EventTrigger>
  <b:EventTrigger EventName="MouseLeave">
 <my:MagnifyAndFocus 
   Factor="1.0" 
   OpacityAfterTrigger="0.5"
  />
  </b:EventTrigger>
 </b:Interaction.Triggers>
</SomeElement>