user2849936
user2849936

Reputation: 73

WPF row height binding stops working after user uses GridSplitter

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.

Upvotes: 6

Views: 1713

Answers (6)

Soleil
Soleil

Reputation: 7457

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" />

Upvotes: 0

HexHead
HexHead

Reputation: 246

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

Upvotes: 2

HexHead
HexHead

Reputation: 246

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.

Upvotes: 4

user2849936
user2849936

Reputation: 73

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.

Upvotes: 0

Nathan Phillips
Nathan Phillips

Reputation: 12455

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.

Upvotes: 0

user1228
user1228

Reputation:

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.

Upvotes: 0

Related Questions