3

Question: How do I bind StatName.Width to Samp1.ActualWidth + Samp2.ActualWidth?

Current vs desired

Sub-questions:

Why are my WidthConverter functions never executed (breakpoints never hit)?

What is the right <Binding ... syntax for the individual MultiBinding components?

What I've tried:

I think I need to assign the DataGridTextColumn.Width with MultiBinding and some type of converter.

I have tried putting together a solution from various examples online. I can't seem to pull it all together.

EDIT

With using <Binding ElementName="Samp1" Path="ActualWidth" />

  1. No build errors or warnings.
  2. The Convert function is not hit
  3. No relevant messages in Output

With using <Binding Source="{x:Reference Samp1}" Path="ActualWidth" />

  1. Build error (that still allows execution): Specified cast is not valid. For <MultiBinding>...</MultiBinding>.
  2. Convert is called three times when loading. It is not called when resizing column Samp1.
  3. On each execution of Convert:

System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property.; Value='40' MultiBindingExpression:target element is 'DataGridTextColumn' (HashCode=64879470); target property is 'Width' (type 'DataGridLength')


MultiBinding Attempt (xaml part)

<Page.Resources>
    <local:WidthConverter x:Key="WidthConverter" />
</Page.Resources>

<StackPanel>

    <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>
                    <MultiBinding Converter="{StaticResource WidthConverter}">
                        <Binding ElementName="Samp1" Path="ActualWidth" />
                        <Binding ElementName="Samp2" Path="ActualWidth" />
                    </MultiBinding>
                </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>

</StackPanel>

MultiBinding Attempt (code part)

public class WidthConverter : IMultiValueConverter
{

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double totalWidth = 0;

        foreach (DataGridLength Width in values)
            totalWidth += Width.Value;

        return totalWidth;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}
Steven
  • 13,501
  • 27
  • 102
  • 146
  • Are you sure `ActualWidth` isn't a `double`? I tried and change form `DataGridLength` to `double` and it worked. – P.Manthe Dec 12 '17 at 01:18
  • @Bob I tried `foreach ( double Width in values ) totalWidth += Width;`. The code compiled and ran, but the `StatName` column was still only as wide as the word *Average* – Steven Dec 12 '17 at 01:45
  • @Bob I discovered that neither `WidthConverter` function is never called. Do you know why? Also, did my code sample execute correctly for you? – Steven Dec 12 '17 at 18:58
  • OK, I misunderstood your problem and I can now reproduce it. [I found some information here](https://stackoverflow.com/questions/9313586/binding-datagrid-column-width). I will try to find a way to solve it and let you know. – P.Manthe Dec 13 '17 at 00:58
  • @Steven: The converter probably is not hit because the binding throws an error first. Always make sure to check the `Output` window for binding errors. – H.B. Dec 13 '17 at 11:53
  • @H.B. With your help, I finally got the Binding and Converter working (see answer below). However, the Visual Studio reports the error `Specified cast is not valid` on my MultiBinding statement. Do you happen to know why? – Steven Dec 13 '17 at 15:57
  • @Steven: Probably because VS is stupid. Usually such errors are caused by the GUI designer, which i never even use. – H.B. Dec 13 '17 at 17:24

1 Answers1

1

The code finally executes as expected with the following:

  • <Binding Source="{x:Reference Samp2}" Path="ActualWidth" />
  • return new DataGridLength(totalWidth);

The Converter gets called on load and when resizing Samp1 or Samp2. The column widths remain synchronized as expected.

<DataGridTextColumn x:Name="StatName"  Binding="{Binding a}" Header="Stat">
   <DataGridTextColumn.Width >
       <MultiBinding Converter="{StaticResource WidthConverter}">
           <Binding Source="{x:Reference Samp1}" Path="ActualWidth" />
           <Binding Source="{x:Reference Samp2}" Path="ActualWidth" />
       </MultiBinding>
   </DataGridTextColumn.Width>
</DataGridTextColumn>

The Convert function needed to return a DataGridLength, the data type of DataGridTextColumn.Width.

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

    double totalWidth = 0;

    foreach (double Width in values)
        totalWidth += Width;

    return new DataGridLength(totalWidth);
}

Note: The code executes as expected, regardless of the error Specified cast is not valid.

  1. The Visual Studio designer underlines the entire tag <MultiBinding ... </MultiBinding> in cyan.
  2. The Error List window reports the error "Specified cast is not valid."
  3. While it is displayed as an error, Visual Studio will still build and execute the code.
Steven
  • 13,501
  • 27
  • 102
  • 146