DataGrid RowDetails Width problem

2019-02-06 08:57发布

Suppose I have a DataGrid that is defined like this

<DataGrid AreRowDetailsFrozen="True"
          ItemsSource="{Binding MyCollection}"
          AutoGenerateColumns="False">
    <DataGrid.RowDetailsTemplate>
        <DataTemplate>
            <Border CornerRadius="5" BorderBrush="Red"
                    BorderThickness="2" Background="Black">
                <TextBlock Foreground="White" Text="{Binding RowDetails}"
                           TextWrapping="Wrap"/>
            </Border>
        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
    <DataGrid.Columns>
        <DataGridTextColumn Header="0" Binding="{Binding Value1}"/>
        <DataGridTextColumn Header="1" Binding="{Binding Value2}"/>
        <DataGridTextColumn Header="2" Binding="{Binding Value3}"/>
        <DataGridTextColumn Header="3" Binding="{Binding Value4}"/>
    </DataGrid.Columns>
</DataGrid>

And looks like this with and without RowDetails

alt text

In the picture to the right I get a very long DataGridRow that never wraps.
Is it possible to get the RowDetails to use the same width as the DataGrid and not effect the Width itself?

Things I have tried that achieves wrapping but not in a satisfying way

  • Set Width or MaxWidth on the Border or the TextBlock. Not very dynamic.
  • Set ScrollViewer.HorizontalScrollBarVisibility="Disabled" on the DataGrid. Not very good when the columns doesn't fit.

6条回答
闹够了就滚
2楼-- · 2019-02-06 09:41

Thanks Meleak, your solution worked well for me. One small addition for us WPF newbies. Be sure to declare your Converter class as a resource so it can be referenced in the Binding expression.

I put mine in App.Xaml like this:

<Application x:Class="ISCBilling.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:conv="clr-namespace:my.application.namespace"
             StartupUri="IscBillingWindow.xaml">
    <Application.Resources>

        <conv:RowDetailsWidthMultiConverter x:Key="RowDetailsWidthMultiConverter" />

    </Application.Resources>
</Application>
查看更多
聊天终结者
3楼-- · 2019-02-06 09:44

This is what I ended up doing: binding of the row details width to the actual width of its presenter and then added a border with a varied thickness to compensate for the presence/absence of a vertical scroll-bar in the presenter. This approach worked perfectly for me. Sample xaml:

<DataGrid.RowDetailsTemplate>
     <DataTemplate>
        <Border BorderThickness="2,2,8,2"
                Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsPresenter}}, Path=ActualWidth}"
                HorizontalAlignment="Left" >
           <!-- add the row details view contents here -->
         </Border>
     </DataTemplate>
</DataGrid.RowDetailsTemplate>
查看更多
太酷不给撩
4楼-- · 2019-02-06 09:46

The answers here felt like a workaround so I did some research and did find the solution on the Telerik forums, since we use their RadGridView. Turned out the solution worked for DataGrid as well.

The key is to set the ScrollViewer.HorizontalScrollBarVisibility property to Disabled, see example below.

<DataGrid ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<DataGrid.RowDetailsTemplate>
    <DataTemplate>
        <Border>
            <TextBlock Foreground="White" Text="{Binding RowDetails}"
                       TextWrapping="Wrap"/>
        </Border>
    </DataTemplate>
</DataGrid.RowDetailsTemplate>

Edit: A side effect is that if the columns needs more space horizontally than there are room for they will be clipped. So if this is a problem then this solution isn't optimal.

查看更多
▲ chillily
5楼-- · 2019-02-06 09:46

You might be able to bind the MaxWidth to ElementName=PART_ColumnHeadersPresenter, Path=ActualWidth or perhaps RenderSize.Width. I believe that is the part of the DataGrid Template that displays the columns so in theory it should work

查看更多
疯言疯语
6楼-- · 2019-02-06 09:55

This is what I ended up doing. I'd rather use a Property on the DataGrid for this but since no such Property exist I needed a workaround.

alt text

First I just used ActualWidth from the parent DataGrid and removed a constant of 9. This worked at first but failed when the vertical scrollbar became visible so I had to use a MultiBinding.

<DataGrid.RowDetailsTemplate>
    <DataTemplate>
        <Border HorizontalAlignment="Left" CornerRadius="5"
                BorderBrush="Red" BorderThickness="2" Background="Black">
            <Border.Width>
                <MultiBinding Converter="{StaticResource RowDetailsWidthMultiConverter}"
                              ConverterParameter="9">
                    <Binding RelativeSource="{RelativeSource AncestorType={x:Type DataGrid}}"
                             Path="ActualWidth"/>
                    <Binding RelativeSource="{RelativeSource AncestorType={x:Type ScrollViewer}}"
                             Path="ComputedVerticalScrollBarVisibility"/>
                </MultiBinding>
            </Border.Width>
            <TextBlock Foreground="White" Text="{Binding RowDetails}" TextWrapping="Wrap"/>
        </Border>
    </DataTemplate>
</DataGrid.RowDetailsTemplate>

And in the converter I used another constant (16) to compensate for a visible vertical scrollbar (if it's visible).

public class RowDetailsWidthMultiConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double originalWidth = (double)values[0];
        Visibility verticalScrollbarVisibility = (Visibility)values[1];
        double subtractWidth = System.Convert.ToDouble(parameter);
        double returnWidth = originalWidth - subtractWidth;
        if (verticalScrollbarVisibility == Visibility.Visible)
        {
            return returnWidth - 16;
        }
        return returnWidth;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

Update

I improved on the solution a bit, using ActualWidth for the ItemsPresenter rather then DataGrid (where ActualWidth didn't change depending on a visible ScrollBar), thus removing the need for a MultiConverter and two constants.

<DataGrid.Resources>
    <local:SubtractConstantConverter x:Key="SubtractConstantConverter"/>
</DataGrid.Resources>
<DataGrid.RowDetailsTemplate>
    <DataTemplate>
        <Border HorizontalAlignment="Left" CornerRadius="5"
                BorderBrush="Red" BorderThickness="2" Background="Black"
                Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsPresenter}},
                                Path=ActualWidth,
                                Converter={StaticResource SubtractConstantConverter},
                                ConverterParameter=6}">
            <TextBlock Foreground="White" Text="{Binding RowDetails}" TextWrapping="Wrap"/>
        </Border>
    </DataTemplate>
</DataGrid.RowDetailsTemplate>

SubtractConstantConverter

public class SubtractConstantConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double originalValue = (double)value;
        double subtractValue = System.Convert.ToDouble(parameter);
        return originalValue - subtractValue;
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}
查看更多
倾城 Initia
7楼-- · 2019-02-06 09:59

To save other folks some head scratching and trial-and-error time:

After fussing with Fredrik Hedblad's most recent (1/1/11) solution for some time I figured out that the ConverterParameter value should be 6 + [left margin} + [right margin] (i.e. margins of the outermost container in the template.) After examining a blowup of a screen shot, I expect the 6 is the width of the vertical bar at the left of each row.

查看更多
登录 后发表回答