4

The code below binds column widths: Samp1.ActualWidth and Samp2.ActualWidth to StatName.Width. See: Bind DataGrid Column Width to Two Colums of Another DataGrid

However, the Designer reports the error Specified cast is not valid., but in spite of the error, the code still compiles and executes as expected.

Even though it runs as expected, the "error" still bugs me.

Question: What is causing the error: Specified cast is not valid.? What is being cast to what? How can I fix it? If a fix is not straightforward, how can I at least hide or ignore the error?

Note: The question WPF MultiBinding VS designer exception is similar. However, that code causes a runtime exception, while my code causes a build error and runs fine (without any exceptions thrown).

Xaml:

<Page.Resources>
    <local:WidthConverter x:Key="WidthConverter" />
</Page.Resources>
<!--- ... -->
<DataGrid IsReadOnly="True" HeadersVisibility="Column">
    <DataGrid.Columns>
        <DataGridTextColumn x:Name="Samp1" Binding="{Binding a}" Header="S1"  />
        <DataGridTextColumn x:Name="Samp2" Binding="{Binding b}" Header="S2"  />
        <DataGridTextColumn x:Name="Total" Binding="{Binding c}" Header="Tot" />
    </DataGrid.Columns>
    <local:MyGenericRecord a="5000" b="2500" c="7500" />
    <local:MyGenericRecord a="1000" b="1500" c="2500" />
</DataGrid>

<DataGrid IsReadOnly="True" HeadersVisibility="Column">
    <DataGrid.Columns>
        <DataGridTextColumn x:Name="StatName"  Binding="{Binding a}" Header="Stat">
            <DataGridTextColumn.Width >
                <!-- ####################################### -->
                <!-- Begin error: Specified cast is invalid. -->
                <MultiBinding Converter="{StaticResource WidthConverter}">
                    <Binding Source="{x:Reference Samp1}" Path="ActualWidth" />
                    <Binding Source="{x:Reference Samp2}" Path="ActualWidth" />
                </MultiBinding>
                <!-- End error -->
                <!-- ###################################### -->
            </DataGridTextColumn.Width>
        </DataGridTextColumn>
        <DataGridTextColumn x:Name="StatValue" Binding="{Binding b}" Header="Val" Width="{Binding ElementName=Total, Path=ActualWidth}" />
    </DataGrid.Columns>
    <local:MyGenericRecord a="Min" b="2500" />
    <local:MyGenericRecord a="Max" b="7500" />
    <local:MyGenericRecord a="Average" b="5000" />
</DataGrid>

Converter:

public class WidthConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {

        double totalWidth = 0;

        foreach (double Width in values)
            totalWidth += Width;
        DataGridLength outLen = new DataGridLength(totalWidth);
        return outLen;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        return new object[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue};
    }
}

public class MyGenericRecord
{
    public string a { get; set; }
    public string b { get; set; }
    public string c { get; set; }
}
Steven
  • 13,501
  • 27
  • 102
  • 146
  • 4
    Check type of items in values array in converter. Most likely some of them are DependencyProperty.UnsetValue and not double. To test - change to foreach (double width in values.OfType()). – Evk Dec 13 '17 at 17:56
  • 1
    I second the check for `UnsetValue`. It's the most unexpected thing to come across the first time. Could also be caused by your `ConvertBack`. If you aren't actually using it to convert back, it's usually best to leave it throwing a NotSupportedException – TyCobb Dec 13 '17 at 18:01

1 Answers1

3

In any converter you should always expect value (or values) can be DependencyProperty.UnsetValue. This value is used by wpf when actual value cannot be obtained for whatever reason. Most often it happens when binding cannot be resolved (for example - you bind to property which does not exist). WPF designer does not compile the whole code, so it might misbehave, especially with bindings. Note that null could not be used instead of UnsetValue, because null can be a valid value. So expect UnsetValue and if possible, try to do something meaningful in that case. For example:

public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    double totalWidth = 0;
    foreach (double Width in values.OfType<double>())
        totalWidth += Width;
    DataGridLength outLen = new DataGridLength(totalWidth);
    return outLen;
}

This will ignore all values which are not double (including UnsetValue).

Evk
  • 98,527
  • 8
  • 141
  • 191