Bind DataGrid Column Width to Two Colums of Another DataGrid
Asked Answered
S

1

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;
    }
}
Selimah answered 11/12, 2017 at 21:59 Comment(7)
Are you sure ActualWidth isn't a double? I tried and change form DataGridLength to double and it worked.Denominational
@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 AverageSelimah
@Bob I discovered that neither WidthConverter function is never called. Do you know why? Also, did my code sample execute correctly for you?Selimah
OK, I misunderstood your problem and I can now reproduce it. I found some information here. I will try to find a way to solve it and let you know.Denominational
@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.Yellowbird
@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?Selimah
@Steven: Probably because VS is stupid. Usually such errors are caused by the GUI designer, which i never even use.Yellowbird
S
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.
Selimah answered 13/12, 2017 at 15:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.