0

I am using MultiBinding and a MultiValueConverter on a Grid in order to set the MaxHeight of each Grid.Row according to the ActualHeight of all Grid.Rows.

               <RowDefinition x:Name="grdRow1" Height="Auto" >
                    <RowDefinition.MaxHeight>
                        <MultiBinding Converter="{StaticResource CalculateMaxHeightConverter}" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
                            <Binding Path="ActualHeight" ElementName="grdRow1" />
                            <Binding Path="ActualHeight" ElementName="grdRow2" />
                            <Binding Path="ActualHeight" ElementName="grdRow3" />
                            <Binding Path="ActualHeight" ElementName="grdRow4" />
                            <Binding Path="ActualHeight" ElementName="grdRow5" /> 
                        </MultiBinding>
                    </RowDefinition.MaxHeight>
                </RowDefinition>

MultiConverter:

public class AvailableHeightConverter : IMultiValueConverter
{
    public double PanelHeight { get; set; }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values == null)
            return Binding.DoNothing;

        double currentHeight = 0d;
        double availableHeight = 0d;

        foreach (object value in values)
        {
            currentHeight += (double)value;
        }

        availableHeight = PanelHeight - currentHeight;

        if (availableHeight < 0)
            return Binding.DoNothing;

        return availableHeight;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

UserControl.Resources:

<cvc:AvailableHeightConverter x:Key="CalculateMaxHeightConverter" PanelHeight="1080" />

My problem (a dummy one) is that i cannot figure out how to UpdateSourceTrigger whenever the ActualHeight of a Grid.Row is being changed.

Zoti
  • 822
  • 11
  • 31
  • try implemting the Interface INotifyPropertyChanged to your class. – Felix D. Nov 09 '15 at 07:53
  • this will automatically notify the UI if some property changes – Felix D. Nov 09 '15 at 07:54
  • but it is not required in a MultiValueConverter – Zoti Nov 09 '15 at 07:56
  • but you need something to trigger the UpdateSource ? don't you ? – Felix D. Nov 09 '15 at 07:58
  • yeap but, IMultiValueConverter should be able to detect any changes to the `Binding`s passed as `values[]` – Zoti Nov 09 '15 at 08:03
  • Please share your complete Xaml, so we can test it ourself and find a solution. If we see what you are trying to achieve we maybe know another way. – christoph Nov 09 '15 at 09:46
  • @christoph there is nothing more to share. it is just a `Grid` with 5 `Grid.Rows`. What i am trying to achieve is to set the `MaxHeight` of each row according to the `ActualHeight` of all the rows. The code above works great but only once (after the `UserControl` is loaded), when i change the height of one of the rows the `Convert(...)` of the `MultiValueConverter` is not being reached. That means that `ActualHeight` doesn't update in order to be catched by the `MultiBinding`. – Zoti Nov 09 '15 at 09:54
  • Why don´t you bind to the Grids ActualHeight property instead? The sum of the heights of all rows should be equivalent to it. – christoph Nov 09 '15 at 09:58
  • @christoph i also tried to set a fixed `Height` for the `Grid` and set the `Height` of each row to `*` in `RowDefinition` (nice and simple) but that sets the same `Height` in each row, so the `Grid` can fit them all. But this is not what i want. I want each row to take all the available size. – Zoti Nov 09 '15 at 09:58
  • @christoph that was what i've tryied first, but it wasn't working properly. I think the problem with the `ActualHeight` property is being described better [here](http://stackoverflow.com/questions/1083224/pushing-read-only-gui-properties-back-into-viewmodel/1083733#1083733) – Zoti Nov 09 '15 at 10:01
  • @Απόπατος You want every row to size with its content until the available space exceeds, is that correct? – christoph Nov 09 '15 at 10:04
  • What if the available space isn´t enough to show all rows: Should they share the available space evenly? Or shall affected controls flow out of sight? – christoph Nov 09 '15 at 10:12
  • Each row contains a `TextBox` with `AcceptsReturn="True"` and `TextWrapping="Wrap"`. The row should get as much size as it can but all rows should be visible. For example if the `Grid`s `Height` is 100 i should be able to have grdRow1 Height = 60, grdRow2 Height = 10, grdRow3 Height = 10, grdRow4 Height = 10, grdRow5 Height = 10 etc. – Zoti Nov 09 '15 at 10:21

2 Answers2

0

There are two problems with your xaml (as far as I can see without trying it myself)

  1. ActualHeight has no setter, so you need to use Mode="OneWay"
  2. Binding the MaxHeight of grdRow1 to all rows ActualHeight including grdRow1 itself could produce an endless loop of resizing

Try the following:

            <RowDefinition x:Name="grdRow1" Height="Auto" >
                <RowDefinition.MaxHeight>
                    <MultiBinding Converter="{StaticResource CalculateMaxHeightConverter}" Mode="OneWay" UpdateSourceTrigger="PropertyChanged">
                        <Binding Path="ActualHeight" ElementName="grdRow2" Mode="OneWay" />
                        <Binding Path="ActualHeight" ElementName="grdRow3" Mode="OneWay" />
                        <Binding Path="ActualHeight" ElementName="grdRow4" Mode="OneWay" />
                        <Binding Path="ActualHeight" ElementName="grdRow5" Mode="OneWay" /> 
                    </MultiBinding>
                </RowDefinition.MaxHeight>
            </RowDefinition>
christoph
  • 1,019
  • 1
  • 12
  • 23
  • Still not updating when the `ActualHeight` changes. I think it has to do with the read-only issue of `ActualHeight` Property as described [here](http://stackoverflow.com/questions/1083224/pushing-read-only-gui-properties-back-into-viewmodel/1083733#1083733). – Zoti Nov 09 '15 at 09:40
0

OK. After experimenting with various senarios i ended up that Binding row's ActualHeight doesn't notify MultiBinding for changes. Also what @christoph noticed (2.) was very helpful. So i ended up binding TextBox's ActualWidth as shown below:

for Row 1

            <RowDefinition x:Name="grdRow1" Height="Auto" >
            <RowDefinition.MaxHeight>
                <MultiBinding Converter="{StaticResource CalculateMaxHeightConverter}">
                    <Binding Path="ActualHeight" ElementName="txtBox2" Mode="OneWay" />
                    <Binding Path="ActualHeight" ElementName="txtBox3" Mode="OneWay" />
                    <Binding Path="ActualHeight" ElementName="txtBox4" Mode="OneWay" />
                    <Binding Path="ActualHeight" ElementName="txtBox5" Mode="OneWay" /> 
                </MultiBinding>
            </RowDefinition.MaxHeight>
        </RowDefinition>

for Row 2

        <RowDefinition x:Name="grdRow2" Height="Auto" >
            <RowDefinition.MaxHeight>
                <MultiBinding Converter="{StaticResource CalculateMaxHeightConverter}">
                    <Binding Path="ActualHeight" ElementName="txtBox1" Mode="OneWay" />
                    <Binding Path="ActualHeight" ElementName="txtBox3" Mode="OneWay" />
                    <Binding Path="ActualHeight" ElementName="txtBox4" Mode="OneWay" />
                    <Binding Path="ActualHeight" ElementName="txtBox5" Mode="OneWay" /> 
                </MultiBinding>
            </RowDefinition.MaxHeight>
        </RowDefinition>

and subtract the Height of one row from the PanelHeight (of AvailableHeightConverter).

<cvc:AvailableHeightConverter x:Key="CalculateMaxHeightConverter" PanelHeight="1028" />

(Used to be 1080 (-52 MinHeight of a Row)

Zoti
  • 822
  • 11
  • 31