Silverlight 4: Bug in TabControl.TabStripPlacement = Dock.Left and OnApplyTemplate

At work I ran into a bug with the Silverlight TabControl, more specifically when setting the TabStripPlacement to Dock.Left and hiding one of the TabItems in the parent’s OnApplyTemplate.

The way to get to this bug is quite specific. For example, you can’t do it when you’re using a UserControl, since in that kind of class OnApplyTemplate is never called.

You need to build a class which inherits from Control.

public class TabControlTest : Control

Then we have the style of this particular class:

<Style TargetType="TabControlTest:TabControlTest"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="TabControlTest:TabControlTest"> <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="38" /> </Grid.RowDefinitions> <sdk:TabControl TabStripPlacement="{TemplateBinding TabStripPlacement}" Grid.Row="0"> <sdk:TabItem Header="First"> <sdk:TabItem.Content> <TextBlock Text="A" /> </sdk:TabItem.Content> </sdk:TabItem> <sdk:TabItem Header="Second" x:Name="middleTabItem"> <sdk:TabItem.Content> <TextBlock Text="B" /> </sdk:TabItem.Content> </sdk:TabItem> <sdk:TabItem Header="Last"> <sdk:TabItem.Content> <TextBlock Text="C" /> </sdk:TabItem.Content> </sdk:TabItem> </sdk:TabControl> <Button HorizontalAlignment="Center" Grid.Row="1" Content="{TemplateBinding ShowOrHide}" Command="{TemplateBinding ShowOrHideCommand}" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>

As you can see I’ve named the middle TabItem (“middleTabItem”) so I can retrieve it in the OnApplyTemplate:

public override void OnApplyTemplate()
{
    this._middleTabItem = (TabItem) this.GetTemplateChild("middleTabItem");
    this._middleTabItem.Visibility = Visibility.Collapsed;

    base.OnApplyTemplate();
}

Now if the following 2 conditions are matched:

  1. TabStripPlacement is set to Dock.Left (it doesn’t happen on ‘Top’)
  2. We set the Visibility of the said TabItem to Collapsed in the OnApplyTemplate of the surrounding Control

Then we cannot make the TabItem Visible anymore.

To show or hide I use the following code:

this.ShowOrHideCommand = new Command(() =>
                                         {
                                            this.ShowOrHideText = string.Format(SwitchTo, this._middleTabItem.Visibility);
                                            this._middleTabItem.Visibility = this._middleTabItem.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
                                         });

The solution to this is to call the OnApplyTemplate of the TabControl itself so that it recalculates it’s children’s location (or something, I’m not sure):

Give the TabControl a name, fetch it in OnApplyTemplate of your Control:

<sdk:TabControl TabStripPlacement="{TemplateBinding TabStripPlacement}" x:Name="TabControl" Grid.Row="0">
public override void OnApplyTemplate()
{
    this._middleTabItem = (TabItem) this.GetTemplateChild("middleTabItem");
    this._middleTabItem.Visibility = Visibility.Collapsed;
    this._tabControl = (TabControl) this.GetTemplateChild("TabControl");
    base.OnApplyTemplate();
}

And call the _tabControl’s OnApplyTemplate after switching the Visibility to Visible:

this.ShowOrHideCommand = new Command(() =>
                                         {
                                            this.ShowOrHideText = string.Format(SwitchTo, this._middleTabItem.Visibility);
                                            this._middleTabItem.Visibility = this._middleTabItem.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
                                            this._tabControl.OnApplyTemplate();
                                         });

This will make the UI respond correctly. For your convenience I’ve added the SL project, and you can download it here.

Visual Studio 2010 SP1: DebuggerTypeProxy works again! *yay*

A while ago I bumped into a bug in Visual Studio 2010 + Silverlight 4.

While working with a List / Dictionary / … it didn’t show the items in the list, instead it showed the normal ‘raw view’:

No debug view

As you can see, there was no view to see the items.

What you would expect to see is this:

DebugView!

I reported the bug (many others did too!). The problem was that it was fixed in SL3, but not ported to SL4.

Now it’s fixed! This makes my work a lot easier. When we get SP1 @ work too that is!

Cheers!

Enum dependency property caveat

Hi all,

A while ago I was creating a custom control in Silverlight with an enum Dependency property.

It looked like this:

public partial class SomeCustomControl
{
    public static readonly DependencyProperty OnOrOffDependencyProperty = DependencyProperty.Register("OnOrOff", 
                                                                                                    typeof (OnOff), 
                                                                                                    typeof (SomeCustomControl), 
                                                                                                    new PropertyMetadata(OnPropertyChangedCallback));
 
    public SomeCustomControl()
    {
        this.InitializeComponent();
    }
 
    public OnOff OnOrOff
    {
        get
        {
            return (OnOff) this.GetValue(OnOrOffDependencyProperty);
        }
        set
        {
            this.SetValue(OnOrOffDependencyProperty, value);
        }
    }
 
    private static void OnPropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        SomeCustomControl instance = (SomeCustomControl) o;
 
        instance.UpdateBackgroundColor((OnOff) e.NewValue);
    }
 
    private void UpdateBackgroundColor(OnOff newValue)
    {
        switch (newValue)
        {
            case OnOff.On:
                this.Rectangle.Fill = new SolidColorBrush(Colors.Green);
                break;
            case OnOff.Off:
                this.Rectangle.Fill = new SolidColorBrush(Colors.Red);
                break;
        }
    }
}

The enum itself was quite simple:

public enum OnOff
{
    On,
    Off
}

The control just changes background color when you set the enum to On (green), or Off (red). Very simple.

The problem is that the dependency property will take the enum member with number 0 as default, OnOff.On that is. And when setting the value in XAML (or code for that matter) will not update the property backing the dependency property:

Where is the green color? :(

The code to render this is found here:

<EnumProblem:SomeCustomControl OnOrOff="On" Grid.Row="1" />
<EnumProblem:SomeCustomControl OnOrOff="Off" Grid.Row="1" Grid.Column="1" />

As you can see the left one is NOT green.

This is because the default is OnOff.On (value 0), and setting it (again) to On doesn’t trigger the execution of OnPropertyChangedCallback.

We can solve this (of course, otherwise what would be the point of writing this? 😛 )

The first solution you might think about is setting the default value of the Dependency Property’s default value. Maybe –1 for example, so that at least the engine can detect a change.

public static readonly DependencyProperty OnOrOffDependencyProperty = DependencyProperty.Register(“OnOrOff”, typeof (OnOff), typeof (SomeCustomControl), new PropertyMetadata(-1, OnPropertyChangedCallback));

Running the app like this will cause an exception, since it cannot convert the –1 to the OnOff enum.

Error when PropertyMetaData.Default = -1

The other solution is setting the value of the first member to a positive value, which makes the code work:

public enum OnOff
{
    On = 1,
    Off
}

It works!

Now you might think: What’s the difference, the first time it tries to convert –1 to OnOff, and the second time it tried to convert 0 to OnOff, and it both cases it the value doesn’t exist in the enum.

The reason here is that you can have an enum that starts at 1, but you can still set create an instance of that enum with a value of 0.

Enum to 0

But creating the enum with a value of –1 doesn’t work:

-1 doesn't work

This is why setting –1 to the default value of the dependency property doesn’t work (unless you of course have a –1 value in the enum!)

If you want the source, and you know a good place to upload it, please mail me 🙂

Silverlight 4 debugging and Firefox 3.6.4+

I’ve been stuck on this for a few days.

At the moment, Firefox 3.6.4 (and newer) have a new functionality called ‘Crash protection’, which is quite nice.

For customers.

It now runs the plugins in a separate process called ‘plugin-container.exe’ (look in your task manager).

Task manager plugin-container.exe

But for developers it’s quite the hassle, since Visual Studio attaches itself to Firefox but NOT to this process. So no more Silverlight debugging for you!

Luckily there are two options for you!

The first option is the most straight forward, but has to be done each time. Use Visual Studio to attach itself to plugin-container.exe, refresh the website, and BAM, you’re up and running!

Attach to Process

On the next screen click ‘plugin-container.exe’. There might be 2, if so, select the one with ‘Silverlight’ in the ‘Type’ column:

Attach to Process Window

And hit ‘Attach’.

While this solution is adequate for when you need to debug it one time at a day, but for me, I only debug in Firefox, and when necessary I use Internet Explorer. For that you can go to your ‘about:config’ in Firefox, disable ‘dom.ipc.plugins.enabled.npctrl.dll’ (set it to false).