WPF row height binding stops working after user uses GridSplitter
Asked Answered
S

6

6

I have a grid with four rows:

<Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>                
            <RowDefinition Height="{Binding DocumentsHeight}"/>
            <RowDefinition Height="Auto"/> - GRIDSPLITTER
            <RowDefinition Height="{Binding ApprovedDocumentsHeight}" /> 
</Grid.RowDefinitions>

The dynamic resizing of rows works fine, heights are binded to strings with values like "5*". But when the user uses the GridSplitter, the binding stops working, getters are not called after next notify when I want to change the size of rows. Does anybody know where is the problem?

Thanks for help.

Skulk answered 21/11, 2013 at 14:52 Comment(0)
H
4

If you are binding to anything besides a GridLength, the binding will break. You can either bind to a GridLength property like this...

   private double documentsHeight = 100;

   public GridLength DocumentsHeight
    {
        get { return this.GridLength(this.documentsHeight); }
        set { this.documentsHeight = value.Value; }
    }

Also you'll need to set Mode=TwoWay on your binding.

Homocentric answered 10/12, 2013 at 16:20 Comment(1)
Absolutely right. Also you can use an IValueConverter to convert between double and GridLength.Erysipeloid
H
2

The reusable solution would be to use a Converter...

public class DoubleToGridLengthConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double len)
            return new GridLength(len, GridUnitType.Pixel);
        return new GridLength(0, GridUnitType.Auto);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value is GridLength ? ((GridLength)value).Value : 0;
    }
}
Homocentric answered 10/12, 2013 at 16:25 Comment(0)
G
0

Could be lots of things. The splitter has to "take over" row/column definitions in order to change widths/heights. This may result in the binding being removed. It would take some time to spelunk the code and see exactly what is going on, but it's pointless as we already know it fails.

So it may not be possible to do what you want easily. If it were me, I'd express the functionality I want by wrapping it in a UserControl. Expose DependencyProperties on the UserControl for DocumentsHeight and ApprovedDocumentsHeight. I'd add change event handlers to these properties, then adjust the splitter position appropriately from the control's codebehind.

Ginetteginevra answered 21/11, 2013 at 15:12 Comment(1)
I just found out it might work, if I bind to GridLength instead of string, gonna try that first.Skulk
B
0

To do its magic the splitter adjusts the height of the row above it by setting the Height property to an explicit value. This results in a call to SetValue, which removes the binding from the property. You'd have to manually restore the binding to use a change notification to update the row size, although if you're doing this then you might just want to consider setting the Height directly instead of using a binding. Alternatively you can create your own splitter that uses SetCurrentValue on the Height property, which will not overwrite the existing binding.

Blastoff answered 21/11, 2013 at 16:8 Comment(3)
So I got it working by setting the binding to a GridLength property with a two way mode. That way, the grid splitter sets the value correctly and the binding doesnt break.Skulk
In the gridsplitter descendant, how would I call SetCurrentValue and prevent SetValue from being called?Lighter
Don't make a gridsplitter descendant, make a replacement control.Blastoff
S
0

So I got it working by setting the binding to a GridLength property with a two way mode. That way, the grid splitter sets the value correctly and the binding doesnt break.

Skulk answered 22/11, 2013 at 8:28 Comment(0)
N
0

In case the grid width or height has a binding to a GridLength property in the ViewModel (that implements INPC), then the correct converter is:

public class GridLengthConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
        CultureInfo culture) => value;

    public object ConvertBack(object value, Type targetType, object parameter,
        CultureInfo culture) => value;
}

In the View the binding is as such (here it's a row height but it can be another property):

<RowDefinition Height="{Binding TopLength, Mode=TwoWay,
    Converter={StaticResource GridLengthConverter}}"/>

In the resource dictionary, where converters will be mapped to the namespace where the class GridLengthConverter is:

<converters:GridLengthConverter x:Key="GridLengthConverter" />
Negotiate answered 22/12, 2020 at 0:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.