пятница, 28 марта 2008 г.

Making objects "UI'ish".

I'm here again to describe another challenge that I've come across (and successfully solved).

This challenge came up from the fact that my project used some functionality of another parallel project (let me call it “parental project”), which was built on .Net 2.0. Nobody thought of implementing objects in WPF-friendly way of course, why should they? So here is the point - I've got a bunch of classes that I want to render and edit in WPF UI. I want to use data binding. I want to write in elegant way Paul Stovel calls Binding Oriented Programming (here and here). I don't want to write wrappers or inherit from that objects (let’s call them “raw”) every time I come across this case.

And then I've recalled a little hook rock-star Josh Smith found to refresh UI. It is described here. Thiss code solved one huge problem – lack of refresh when you bind your UI to the whole object (in spite of it was implementing INotifyPropertyChanged (INPC) interface). So I've taken that solution and expanded it a little to fit my needs.

The essence of that hook is substitution of wrapper object in place of an expected one. This wrapper raises PropertyChanged event every time UI tries to edit some property and renew itself (the wrapper) forcing UI to refresh. This wrapper is injected in UI via binding converter.

In my case there can be no INPC implementation in an object. Idea, described above, plus the generic type argument are my enhancements to Josh Smith's BuisnessObjectHolder (ObservableObjectWrapper in my version):

This class is a descendant of base class implementing INPC - ObservableObject, that was described by Josh here.

There are 2 internal methods - SetValueProperty и GetValueProperty. They are used by binding converter... later on.

Wrapper accepts a wrapping object in constructor or in Value setter. Then it gets property descriptors list by means of type descriptor and caches them. And it subscribes to a PropertyChanged event in there is one.

So now it is time to describe binding converter that substitutes our wrapper in UI or wrapped object property values, that can be obtained by name in ConverterParameter.

Converter works in the following way:

  • In a forward process (binding source => binding target) it checks if there is a created wrapper for an object (BindingSource) and in case wrapper is not exist yet it creates a new one. It returns the value in UI depending on received parameter: ConverterParameter = null - wrapper; ConverterParameter = propertyName - a value of the property of wrapped object with name = propertyName, by means of GetValueProperty() method from wrapper;

  • In backward process (binding target => binding source) it checks ConverterParameter value and if it is not null calls SetValueProperty() method in wrapper with ConverterParameter and value, recieved from UI, as method parameters.

UI is forced to refresh by the Value_PropertyChanged method (that raises PropertyChanged on the whole object), that is triggered by SetValueProperty() method.

Please note, that first we need to set wrapped object to null and restore it after the first call to SendPropertyChanged. It is necessary to force WPF binding engine to refresh as every time it gets PropertyChanged notification it checks the values and rejects update if it finds them equal.

And now when solution is almost ready one little question comes up - how do I create my generic converter without code-behind in Xaml? And again it struck to me that the guru already wrote about it - Mike Hilberg's post on limited generics support in Xaml. The essence of this approach is to use markup extension to create generic types in Xaml. Here is a slightly modified code of main markup's method:

And at last whole scenario assembled one:

One of disadvantages is you can't reference to this converter from StaticResource anywhere except StaticResource in Style in resources. Otherwise you will have to create this converter every time you use it in every binding.

PS: another approach was demonstrated by Paul Stovell. It uses custom typedescriptor and wrapper that can simulate fake properties and interfaces. This approach is useful not only in WPF but in other frameworks too! Here is the original article - http://www.paulstovell.net/blog/index.php/runtime-ui-binding-behavior-ieditableobject-adapter/

must read.

Good luck!

четверг, 27 марта 2008 г.

Xaml Viewer

Here I am again trying to translate my thoughts.

In my previous post I've mentioned the complex case in localization caused by the WPF document API formatted text in TextBlock. For example:

-, that in Russian looks like this:

There is no possibilities to use markup extension in document objects (such as Run or Bold etc.), because it will raise exceptions in XamlParser. The reason is simple - these document objects are not parts of a visual tree. Therefore I need another way to localize and render this text, so I've decided to write custom control for rendering loose-xaml in run-time and keep this localizable xaml same way as I keep string resources - in resx file and obtain them with the CultureResource markup extension.

Well, there is absolutely nothing complex in this control, and I am mentioning it only in the scope of localizing formatted text scenario.

It works in the following way:

  • control has 2 states: Host (this is our case - static rendering of bounded xaml) and Edit (in this state you can edit xaml and see render output changes in runtime, there will be an edit box in the upper half for xaml editing);
  • it has a string property Xaml;
  • control parses xaml (by means of XamlReader) and render it in its Content property;
  • Content is binded to internal control part - ScrollViewer, so in case your render output is bigger then the control area, you'll see a scrollbar;
The edit mode control template:

The Host mode control template:

In case of exceptions in xaml parsing process (the most propable is XamlParseException) control will catch them and show the exception message in its content area. This process is governed by a trigger in default style.

The error content style:

This is the way Xaml looks in the resources table:

And this is the way our scenario looks like an assembled one:

Disadvantage is lack of xaml formating (since it is just a string property, it can be nonformatted xml - very annoying!).

Good luck!

среда, 26 марта 2008 г.

Safe event pattern

I've found this useful little pattern browsing the code of the latest Prism drop.
It looks like this:

public event EventHandler Updated = delegate { };

I've never declared events in my code this way. But using this little pattern you can forget about the tedious safety event raising checks and write like in the following line of code:

Updated(null, new AccountPositionEventArgs());

instead of:

EventHandler handler = Updated;
if (handler != null)
handler(null, new AccountPositionEventArgs());

I'll remember this great stuff.

Good luck!

вторник, 25 марта 2008 г.

XAML localization 2

I've just found that Actipro uses the same approach. Here.

Good luck!

XAML localization

Hi guys!

At last I've decided to start english version of my blog. Honestly, I don't see any reasons to keep the blog in my native language as it seems to bring no use to anyone.

And it is not a coincidence that my first blog entry is dedicated to localization in WPF.

I've come across localization issues when I started developing presentation layer for my project with WPF. And my firm belief was that English and only English was default language of international applications (surprisingly that MS guys apparently think same way too), but it has crashed

So I started looking for solution and found a number of articles at codeproject and msdn:
WPF Globalization and Localization.

All of these entries solved the problem and suggested ready solutions with its pros and cons (in my sight), but it was not enough. Here I give short description of these approaches:

  • Localization via use of resx files and custom code generator for public resource class (this generator is used due to wpf binding engine restriction - you can bind data only from public properties/fields in public classes) (1st article): pros - supports design-time; easy to use (just create ObjectDataProvider, put your resources' class in it and bind your strings to DEPENDENCY properties of objects in xaml). Cons result from pros (pardon me my English) - first of all we are strongly restricted in use, 'cause binding can be applied ONLY to the dependency properties of the DependencyObject descendants; and second disadvantage is that additional VS installation (custom resource generator tool) is required.
  • Localization via LocBaml (simplified variant - it uses ResourceDictionary with localizable strings that are used by merging this dictionary in Application.Resources.MergedDictionaries and referring to them by means of DynamicResource extension)(1st article): it is still not straightforward enough, because Locbaml forces us to compile binaries twice.
  • Localization via LocBaml (official MS tutorial) (articles #1, #5): see above.
  • The approach in the 3rd article is similar to the 1st item. Main difference here is that article author writes wrappers of resources classes manually. But I've got almost 50 projects in my solution. I don't want to write this bunch of classes manually. (Suddenly I thought that Paul Stovell's idea with custom type descriptors could be useful here... or wrapping could become less complicated via file reference in VS... I'll think about it later... Main advantage of described solution is the ability to switch languages in runtime.
  • Localization via xml files, XmlDataProvider, XPath queries in Binding (4th article): I consider this to be the most elegant solution among all listed here. Pros - design-time support and runtime language switching. The only disadvantage I can think of is complexity of files' maintenance.

Well, after collecting different solutions I came to following requirements:

  • ability to localize clr properties of objects created in xaml;
  • easy use;
  • no need to write wrappers and use custom tools;
  • design-time support (the only requirement here is ability of this xaml to be opened in Blend).

I shaked (not stirred) everything and saw the solution - xaml markupextension + resx files.
The idea is straightforward - keeping all resources in resx files results in easy maintenance of localizable data in VS (.Net 2.0 localization rocks!); in xaml you substitute the string with this markup extension. The extension needs a resource Id that you provide, and it returns back your localized string with this Id in parsing time or error message in case of any exceptions.

It looks like this:

<TextBlock Text={me:CultureResource resourceId}/>
, where me: - namespace declaration where the CultureResourceExtension class declared.

The markupextension doesn't suffer from restriction of DependencyObject and allows localizing any clr object in XAML. Disadvantages are - lack of design-time support, or more properly, highly restricted support (Blend is able to open a file without crashing, but instead of expected strings you can see "ResourceStr", - our designer said it was not critical =) ).
Also resource class name should be directly assigned to the extension in case of:

  • assembly name differs from default namespace;
  • resx file is not in projects /Properties subfolder;
  • in both cases;

The extension has a BaseName property, where you can supply your resources class name.
To simplify this case I've written a helper class BaseBinder with attached property Base that inherits in visual tree.

There exists another case where this solution is not effective: text, formatted with WPF document API. I've solved this problem by putting this formatted xaml in resx file and then rendering it with my custom control XamlViewer.

Here is main extension method:

Good luck!