Alexander Mujirishvili
Alexander Mujirishvili

Reputation: 365

Binding Width to sum of actualWidths' of two elements in WPF

I want to make imitation of Datagrid with multilevel(2 level) column header. I am going to define first level with Grid on top of datagrid and the second level will be datagrid header itself as shown below:

enter image description here

The problem I have now (Which is probably in valueconventer) is that I want the width of GridColumn1 always to be sum of widths' of DataGridColumn1 and DataGridColumn2 columns. For this, I have created value converter:

public class DataGridExtraHeaderConventer : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            int actualWidth = 0;

            foreach (var item in values)
            {
                actualWidth += System.Convert.ToInt32(item);
            }

            return actualWidth;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            var x = DependencyProperty.UnsetValue;
            var y = DependencyProperty.UnsetValue;
            return new object[] { x, y };
        }
    }

And Defined the GridColumn1 as shown below:

<Grid.ColumnDefinitions>
                <ColumnDefinition>
                    <ColumnDefinition.Width>
                        <MultiBinding Converter="{StaticResource DataGridExtraHeaderConventer }">
                            <Binding ElementName="Column1" Path="ActualWidth"/>
                            <Binding ElementName="Column2" Path="ActualWidth"/>
                        </MultiBinding>
                    </ColumnDefinition.Width>
                </ColumnDefinition>
                <ColumnDefinition Width="*" />

Datagrid looks like this:

<DataGrid Name="datagrid1" AutoGenerateColumns="False" Grid.Row="3" RowHeaderWidth="10">
            <DataGrid.Columns>
                <DataGridTextColumn x:Name="Column1" Binding="{Binding something}" Header="column 1" Width="50*" />
                <DataGridTextColumn x:Name="Column2" Binding="{Binding something}"  Header="column 2" Width="60*" />
                <DataGridTextColumn x:Name="Column3" Binding="{Binding something}" Header="column 3" Width="80" />
                <DataGridTextColumn x:Name="Column4" Binding="{Binding something}" Header="column 4" Width="*" />
            </DataGrid.Columns>
        </DataGrid>

This code for some reason doesn't work. I run it successfully, buy the width of GridColumn is as it was without binding.

P.S If I want to bind DataGridColumn1 column's width to GridColumn1, this works:

<ColumnDefinition Width="{Binding ElementName=Column1, Path=ActualWidth}">

Upvotes: 1

Views: 1473

Answers (1)

user2819245
user2819245

Reputation:

The converter in the question has two problems:

  1. The values fed from the ActualWidth properties to the converter are of type double, but the converter converts them into integer values. This will cause rounding errors, causing the converter result to be somewhat inaccurate.

  2. The ColumnDefinition.Width is of type GridLength. The targetType argument of the converter's Convert method will also tell this. Since a integer or floating point value is not implicitely/explictely convertible to GridLength, the converter itself has to produce and return such a GridLength object. Otherwise, the binding mechanism will reject the result produced by the converter.

Addressing the two issues, a working Convert method could look for example like this:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
    try
    {
        double sumActualWidths = values.Sum(v => System.Convert.ToDouble(v));
        if (targetType == typeof(GridLength))
            return new GridLength(sumActualWidths);
        else
            return System.Convert.ChangeType(sumActualWidths, targetType);
    }
    catch (Exception ex)
    {
        Oops! Something went wrong.
        I better handle this exception in a meaningful way.

        return Binding.DoNothing;
    }
}

Upvotes: 1

Related Questions