0

I have a WPF/XAML DataGrid where one column contains images. I want the width of that column (and thus the size of the images) to be controlled by a Slider that is not in the DataGrid. (I am aware this poses a problem in itself, but I found a good solution here.) The trouble is, the binding between the slider Value and the column Width seems very fragile. If the user resizes the parent window, causing the DataGrid to resize, the binding breaks. If I set VerticalScrollBarVisibility="Auto" in the DataGrid, and then the user adjusts the slider so that the DataGrid's scrollbar needs to appear, then the binding breaks. Once the binding is broken, it doesn't come back unless the user closes the window and reopens it.

Here is the relevant DataGrid column definition:

<DataGridTemplateColumn Width="{Binding Source={x:Reference PictureSizeSlider}, Path=Value}">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Image Name="ImageBox" Stretch="Uniform" Source="{Binding Image}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

And here is the Slider definition (inside a different Grid):

<Slider Grid.Column="1" Name="PictureSizeSlider" Minimum="10" Maximum="255" Value="100" IsMoveToPointEnabled="True"/>

Can anybody advise as to why the binding is so fragile, and how I can make it robust?

Morris
  • 126
  • 1
  • 10

1 Answers1

1

Use a Binding Converter that converts between DataGridLength and double:

public class DataGridLengthConverter : IValueConverter
{
    public object Convert(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        var length = (DataGridLength)value;
        return length.UnitType == DataGridLengthUnitType.Pixel ? length.Value : 0d;
    }

    public object ConvertBack(
        object value, Type targetType, object parameter, CultureInfo culture)
    {
        return new DataGridLength((double)value);
    }
}

Apply it to a Binding of the Value property of the Slider:

<DataGridTemplateColumn x:Name="imageColumn" Width="100">
    ...
</DataGridTemplateColumn>

<Slider Value="{Binding Width, 
                ElementName=imageColumn,
                Converter={StaticResource DataGridLengthConverter}}" .../>

In contrast to your original Binding, this one is TwoWay and hence works in both directions, i.e. when you manipulate the Slider or the DataGrid column.

A OneWay Binding is replaced when the bound property is set by a source other than the Binding, e.g. a directly set property value.

Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Amazing! Thanks. Can you explain why it's important to bind the Slider Value to the DataGrid Column Width, rather than just stipulate that the Binding I used should be two-way? Yours is the other way round from what I was doing, and it seems counter-intuitive. – Morris Dec 04 '20 at 16:36
  • I chose this way to avoid `{x:Reference ...}`. It should however also work the other way round, but you would have to set `Mode=TwoWay` explicitly. – Clemens Dec 04 '20 at 16:43
  • Could you explain why {x:Reference ...} is a poor choice and should be avoided? – Morris Dec 04 '20 at 16:59
  • Not really. It just seems uncommon. – Clemens Dec 04 '20 at 17:00