Bind DataGrid Column Width to Two Colums of Anothe

2019-02-25 04:33发布

问题:

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

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;
    }
}

回答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.