Silverlight and WPF Single-Sourcing – XAML namespaces

I have found that WPF and Silverlight XAML are sufficiently different to make single-sourcing difficult.

At first I did not know if it was possible at all, but I knew one thing – the differences in usage of namespaces look like something very annoying for which there must be an elegant solution.

The Problem

The first issue was with the standard controls which are in WPF 4.0 but not in Silverlight 3.0 (only in the toolkit), e.g. the Expander is declared like this in WPF,

<Expander … />

But it is declared like this in Silverlight,

… xmlns:Controls1="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" …
<
Controls1:Expander … />

What I really did not understand was that there is a similar problem when the standard controls which are in WPF 4.0 also are in Silverlight 3.0; for example, the HierarchicalDataTemplate is declared like this in WPF,

<HierarchicalDataTemplate … />

But it is declared like this in Silverlight,

… xmlns:Windows="clr-namespace:System.Windows;assembly=System.Windows.Controls" …
<
Windows:HierarchicalDataTemplate … />

Clearly the rules for defaulting and declaring namespaces are different.

The First Solution

I searched the Internet and found a few suggestions. One suggestion is to have two sets of name space references. This works because you are allowed to use name spaces on WPF although it is not mandatory. I did not like it because it still meant maintaining different files since XAML does not allow #IFDEF-like constructs.

My colleague David Anson found a surprisingly simple solution which he describes in a blog post (see the last bullet in the notes at the end of the post). The main idea is to subclass standard controls, effectively bringing them into the same namespace across Silverlight and WPF.

It looks like magic – and ReSharper also claims that this is not valid – but it works. Well, it almost works.

The Problem with the First Solution

So far I have found one problem with the first solution – the HierarchicalDataTemplate.

If I used David’s trick,

public class HierarchicalDataTemplate : System.Windows.HierarchicalDataTemplate { }

xmlns:Std="clr-namespace:Microsoft.Dynamics.Ax.Frameworks.Controls.StandardControls"

41: <Std:HierarchicalDataTemplate x:Key="hierarchicalDataTemplate" ItemsSource="{Binding Path=Subcomponents}">
42:    <StackPanel Orientation="Horizontal">
43:        <
Image Source="{Binding Path=TreeIcon}"/>
44:        <TextBlock Text="{Binding Path=Name}"/>
45:    </StackPanel>
46: </
Std:HierarchicalDataTemplate>

Then it compiled in Silverlight and WPF but the StackPanel caused problems at run-time in WPF. The error was

‘StackPanel’ object cannot be added to ‘HierarchicalDataTemplate’. Object of type ‘System.Windows.Controls.StackPanel’ cannot be converted to type ‘System.Windows.FrameworkElementFactory’.  Error at object ‘System.Windows.Controls.StackPanel’ in markup file ‘Microsoft.Dynamics.Ax.Frameworks.Controls;component/singlesourcetest.xaml’ Line 42 Position 26.

It did not help to use David’s trick again and add StackPanel to the Std namespace.

This is a WPF bug. It has not been fixed in the WPF 4.0 beta I am using but I hope it will be fixed eventually in the RTM.

The Second Solution

David worked hard on this problem and got another brilliant idea; if this is a problem with WPF only then why not subclass in Silverlight only? This requires a little trick that left David "a little slimy" – see his blog post for the details – but it works. Well, it almost works.

The Problem with the Second Solution

I took the sample project from the blog post for a spin and found that it would not work if I added x:Name="myTreeview" to the TreeView in XAML. This is the error I got,

The type ‘System.Windows.Controls.TreeView’ exists in both ‘c:\Program Files\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\System.Windows.Controls.dll’ and ‘e: \SharingXamlSilverlightWpf\PresentationFramework\Bin\Debug\PresentationFramework.dll’                e:\enlistments\axmain6\source\frameworks\controls\sharingxamlsilverlightwpf\sharingxamlsilverlightwpf-sl\obj\debug\mainpage.g.cs      38           42           SharingXamlSilverlightWpf-SL

Clearly there is a problem with disambiguating identical names across assemblies.

I needed to reference the name of the tree from a details view so I could not use the second solution for at least the controls that I needed to name.

But since the second solution seemed to only be needed for HierarchicalDataTemplate anyway, I am now using the first solution for everything but the HierarchicalDataTemplate.

And this works. Well, it almost works. But the remaining problems seem to be unrelated to this single-sourcing approach so I will defer blogging about it till later.

Advertisement

WPF: How to mark an input field as not valid

I wanted to add to my input controls a visual clue that tells if the current content of the control is valid.

The visual clue I had in mind was a red squiggly line, similar to the way MS Word underlines my typos.

Why this particular visual clue? Because this is how it is done in MS Dynamics AX,

clip_image002[4]

This left me with two tasks,

1. How to trigger the actual validation.

2. How to change the default WPF visual clue (a red border around the control) to the desired red squiggly line.

How to trigger validation

I thought about using validation rules similar to what Nigel Spencer blogged about. But validation is only triggered when the binding value has changed, while I wanted the visual clue to work even at start-up.

I was not alone with this problem. Willem Meints blogged about this as well as about several other validation problems. He actually wrote a complete validation framework that I found would be too much of a good thing for me. He also wrote that he did not use IDataErrorInfo as it does not provide enough flexibility.

I like to keep things simple, so I opted to use IDataErrorInfo.

Basically, I let the business object I bind to report an error if it is not happy about what the user typed so far. It can be augmented by validation rules, e.g. a user defined NumberRangeValue.

For the simple case, meaning the check for mandatory fields having a value, I use code similar to the following,

public string this[string columnName]
{
    get
    {
        if (string.IsNullOrEmpty(columnName))
        {
            return string.Empty;
        }

        if (<this is a mandatory field and has no value>)
        {
            // This is never shown in the UI and does not
            // have to be localized; but if we ever choose
            // to show this text it must be localized!
            return "This field is mandatory.";
        }

        return string.Empty;
    }
}

Now I need to figure out how to trick the visual of controls to show a squiggly red line instead of the default red border.

How to get the red squiggly line

The TextBlock control can actually do the trick with the red squiggly line with its built-in spell checker. Alas, there is no API to trigger this at will.

Instead I found that the following XAML placed in a Resources section would do the trick,

<DrawingBrush x:Key="squiggleBrush" TileMode="Tile"
        Viewbox="0,0,4,4" ViewboxUnits="Absolute"
        Viewport="0,0,4,4" ViewportUnits="Absolute">
    <DrawingBrush.Drawing>
        <GeometryDrawing Geometry="M 0,2 L 1,1 3,3 4,2">
            <GeometryDrawing.Pen>
                <Pen Brush="Red" Thickness="1"
                StartLineCap="Square" EndLineCap="Square"/>
            </GeometryDrawing.Pen>
        </GeometryDrawing>
    </DrawingBrush.Drawing>
</DrawingBrush>
<ControlTemplate x:Key="SquiggleError">
<StackPanel HorizontalAlignment="Center" VerticalAlignment=
        "Center">
        <AdornedElementPlaceholder/>
        <Rectangle Height="4" Fill=
            "{StaticResource squiggleBrush}"/>
    </StackPanel>
</ControlTemplate>
For e.g. a combo box I apply this template as follows,
 
<ComboBox >
    <
Validation.ErrorTemplate>
        <
DynamicResource ResourceKey=”SquiggleError”/> </Validation.ErrorTemplate>
</
ComboBox>
All this ensures that the red squiggly line is drawn when I want it, and it looks close enough to the real thing,

clip_image004[4]

WPF: Making combo box items disabled – also when accessed using the keyboard

When I first embarked on WPF I ran into a number of small problems. Here is one of them.

The problem

I tried various ways to make my data bound combo box items disabled. I found that binding the ComboboxItem.IsEnabled to a property that indicates whether the item should be enabled did the trick.

But this did not work when I selected items using the keyboard. I also tried to make these not focusable but with the same result.

What worked: Click the combo down with the mouse and see that items that must be disabled can in fact not be selected.

What not worked: Use tab and arrow down to select items. They are not disabled and I can select them just fine with keyboard keys. Funny enough, if I click the combo down with the mouse, then the items are in fact disabled and cannot be selected with the keyboard keys.

Here is the XAML (I set up the data, meaning the properties FieldDomainValues, IsSelectable and ValueAsString, in constructers in code-behind):

<ComboBox x:Name="cmbx_test" ItemsSource="{Binding Path=FieldDomainValues}">
    <ComboBox.ItemContainerStyle>
        <Style>
            <Style.Triggers>
                <DataTrigger Binding ="{Binding IsSelectable}" Value="False">
                    <Setter Property="ComboBoxItem.Focusable" Value="False"/>
                    <Setter Property="ComboBoxItem.IsEnabled" Value="False"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ComboBox.ItemContainerStyle>
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=ValueAsString }"></TextBlock>
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

What did I do wrong here?

The workaround

Before we dig into the core reason for this problem, let’s have a look at a simple work-around that Parag Bhand suggested,

We also faced similar issues while dealing with binding in combobox. Once we open the dropdown every thing works fine (even from keyboard) then onwards. I guess it has something to do with ItemsContainerGenerator because item containers are not generated until dropdown is opened at least once.

In fact if we open the drop down and close it again programmatically in window’s loaded event handler then also it works fine.

cmbx_test.IsDropDownOpen = true;
cmbx_test.IsDropDownOpen = false;

This is what I do now and it works.

The explanation

Dwayne Need, found the explanation,

ComboBox.SelectItemHelper has logic to determine if the item and its corresponding container allow selection. When there is no container, this logic always returns True. The containers (ComboBoxItems) aren’t created until the dropdown box is measured, which doesn’t happen until the user causes the dropdown to appear, typically by clicking on its down-arrow button.

If you followed that, it’ll be clear why the workaround works. It causes a measure on the dropdown, which generates the containers. After that, SelectItemHelper’s logic picks up the state the app has declared for the containers. It should also be clear why we don’t do that for you automatically – the workaround creates UI that is never displayed.

This looks like a scenario bug to me. While the technical details make sense, the end-to-end scenario is broken.

Please file a bug.

This has now been filed as a bug (750993: Unable to disable combo-box items when selected with the keyboard).

Unfortunately the fix for this bug will not make it into .NET 4.0 so we must try and get around with the workaround for now.