Search code examples
c#wpfapp.xaml

Getting an XamlObjectWriterException only at design time with ValueConverter inside DataTemplate (Visual Studio 2015)


I have an application which runs perfectly at run-time but fails at design time with Collection property 'System.Windows.Data.Binding'.'ConverterParameter' is null exception.

I think I have narrowed it down to the fact that I am referencing an x:array static resource (declared in resource dictionary, merged in App.xaml) in a value converter inside a DataTemplate.

If I use the same code outside a DataTemplate, I do not get this problem. I suspect that this question has the same root cause.

I have re-created the problem:

In Resource Dictionary:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:local="clr-namespace:trial_app_for_x_array_issue.Resources">

<x:Array x:Key="VisibilityArrayFalseCollapsed" Type="Visibility">
    <Visibility>Visible</Visibility>
    <Visibility>Collapsed</Visibility>
</x:Array>

In the DataConverter:

using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace trial_app_for_x_array_issue.Converters
{
    public class BoolToVisibilityMultiParamConverter : IValueConverter

    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // Check for design mode.
            if ((bool)(DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue))
            {
                return Visibility.Visible;
            }

            if (value is bool && targetType == typeof(Visibility))
            {
                Array arr = parameter as Array;
                if (null != arr && arr.Length == 2)
                {
                    bool ValueEqTrue = (bool)value;
                    if (ValueEqTrue)
                    {
                        return arr.GetValue(0);
                    }
                    return arr.GetValue(1);
                }
            }
            return Visibility.Visible;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

and in MainWindow:

<Window x:Class="trial_app_for_x_array_issue.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:converters="clr-namespace:trial_app_for_x_array_issue.Converters"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="525"
    Height="350">
<Window.Resources>
    <converters:BoolToVisibilityMultiParamConverter x:Key="MultiParamBoolToVisibilityConverter" />
</Window.Resources>
<Grid>
    <Expander>
        <Expander.HeaderTemplate>
            <DataTemplate>
                <TextBlock Name="CurrentActivityPercentageTextBlock"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"
                           Text="Hello World"
                           Visibility="{Binding CurrentActivities.IsIndeterminate,
                                                Converter={StaticResource MultiParamBoolToVisibilityConverter},
                                                ConverterParameter={StaticResource VisibilityArrayFalseCollapsed}}" />

            </DataTemplate>
        </Expander.HeaderTemplate>
    </Expander>
</Grid>

If I take the TextBox out of the DataTemplate, there are no errors.

Incidentally, if I use another type instead of x:array, there is no exception so it seems (to me at least) to be something to do with the use of x:array resource inside a DataTemplate.

I have now run out of ideas ...


Solution

  • Unfortunately this looks like a bug in the XamlLoader. It seems to struggle with linking its various parsing contexts.
    Not very Dry but, you can fix it by moving the array to the DataTemplate Resources. I'm not sure what you were doing with the binding for Visibility (as mentioned in my comment) so I set the Expander.Header property to an appropriate value in order to establish the correct data context for the DataTemplate...

    <Window x:Class="SO_41650679_2670182.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:converters="clr-namespace:SO_41650679_2670182.Converters"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <RelativeSource x:Key="View" Mode="FindAncestor"
                            AncestorType="{x:Type Window}" />
            <converters:BoolToVisibilityMultiParamConverter x:Key="MultiParamBoolToVisibilityConverter" />
        </Window.Resources>
        <StackPanel>
            <Expander Header="{Binding RelativeSource={StaticResource View}, Path=CurrentActivities.IsIndeterminate}">
                Hello World
                <Expander.HeaderTemplate>
                    <DataTemplate>
                        <DataTemplate.Resources>
                            <x:Array x:Key="VisibilityArrayFalseCollapsed" Type="Visibility">
                                <Visibility>Visible</Visibility>
                                <Visibility>Collapsed</Visibility>
                            </x:Array>
                        </DataTemplate.Resources>
                        <TextBlock Name="CurrentActivityPercentageTextBlock"
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center"
                                   Text="Header"
                                   Visibility="{Binding Converter={StaticResource MultiParamBoolToVisibilityConverter},
                                                    ConverterParameter={StaticResource VisibilityArrayFalseCollapsed}}" />
                    </DataTemplate>
                </Expander.HeaderTemplate>
            </Expander>
            <ToggleButton Name="ToggleButton" Height="30"
                          IsChecked="{Binding CurrentActivities.IsIndeterminate, 
                                      RelativeSource={StaticResource View}}" />
        </StackPanel>
    </Window>
    

    Although this gets rid of the design time error, it causes a warning about No default constructor found for type 'System.Windows.Visibility[]' (I have no idea what it's problem is...) and your design time converter code path doesn't seem to work either.