Fatal exception when clearing ComboBox

Topics: Bugs, Conventions
Jun 24, 2013 at 5:49 PM
One of the projects I am working on repopulates a combo box with new view models while the application is running. This is causing the application to crash. After some investigation, I have managed to reproduce the problem fairly easily. This is the simplest possible reproduction case I could come up with (using the latest Caliburn.Micro, 1.5.2):

App.xaml:
<Application x:Class="ComboBoxTest.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:ComboBoxTest">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary>
                    <local:Bootstrapper x:Key="bootstrapper"/>
                </ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
Bootstrapper.cs:
namespace ComboBoxTest
{
    class Bootstrapper : Caliburn.Micro.Bootstrapper<ShellViewModel> { }
}
ShellViewModel.cs:
namespace ComboBoxTest
{
    class ShellViewModel : Caliburn.Micro.Conductor<object>.Collection.OneActive
    {
        public ShellViewModel()
        {
            this.ActivateItem(new object());
        }

        public void Clear()
        {
            this.Items.Clear();
        }
    }
}
ShellView.xaml:
<Window x:Class="ComboBoxTest.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ShellView" Height="300" Width="300">
    <StackPanel>
        <ComboBox x:Name="Items"/>
        <Button x:Name="Clear" Content="Clear"/>
    </StackPanel>
</Window>
That's the entire project. All you have to do is compile, run, and click the clear button. The app will die as the result of an unhandled ArgumentNullException exception while calling System.Activator.CreateInstance from Caliburn.Micro.BootstrapperBase.GetInstance from Caliburn.Micro.Action.SetTargetCore. Higher up the callstack, we see that Action.Target property is being set because View.Model is being changed. Note that because I'm conducting objects of type System.Object, Caliburn.Micro will fail to locate a view; this is unrelated to the exception, which will occur even if you change the ShellViewModel to conduct a very simple custom view model class.

For the record, this is in Visual Studio 2010, using .NET Framework 4 Client Profile, Caliburn.Micro 1.5.2 (from NuGet), and WPF.

This does not occur if I use a ListView or ListBox. From some other threads, it seems like perhaps there is an issue with combo boxes due to their popup window, but fiddling around with Action.TargetWithoutContext and Bind.ModelWithoutContext failed to fix it (though I easily could have been doing it incorrectly). This thread appears to be discussing the same issue, but no resolution was offered.

This seems to me to be a very simple use case. Is this a bug? Or am I missing something?
Jun 28, 2013 at 6:33 AM
Caliburn.Micro does some magic with the ContentControl, when you remove this it will look like below (except the TextBoxConverter).
The following will work without throwing an exception, but if you remove the converter you see the same error as before.

It has to do how TextBlock is working and Caliburn.Micro is binding it:
  • CM binds to the Text property
  • setting Text to null and afterwards reading text will give you an empty string (this makes the problems)
ShellView.xaml
<Window x:Class="ComboBoxTest.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
        xmlns:local="clr-namespace:ComboBoxTest"
        Title="ShellView" Height="300" Width="300">
    <Window.Resources>
        <local:TextBlockValueConverter x:Key="TextBoxConverter" />
    </Window.Resources>

    <StackPanel>
        <ComboBox x:Name="Items">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <ContentControl cal:View.Model="{Binding Converter={StaticResource TextBoxConverter}}" 
                                    VerticalContentAlignment="Stretch" 
                                    HorizontalContentAlignment="Stretch" 
                                    IsTabStop="False" />
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
        
        <Button x:Name="Clear" Content="Clear"/>
    </StackPanel>
</Window>
TextBlockValueConverter.cs
using System;
using System.Globalization;
using System.Windows.Data;

namespace ComboBoxTest {
    public class TextBlockValueConverter : IValueConverter {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
            var stringValue = value as string;
            if (stringValue != null && stringValue.Length == 0)
                return null;

            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
            return value;
        }
    }
}