DataGrid行详细信息宽度问题

13

假设我有一个这样定义的 DataGrid

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

有和没有RowDetails的区别如下所示:

alt text

在右侧的图片中,我得到了一个非常长的DataGridRow,它从未换行。
是否可以使RowDetails使用与DataGrid相同的宽度,而不影响Width本身?

我尝试过的一些方法可以实现换行,但并不是很令人满意:

  • 在边框或TextBlock上设置Width或MaxWidth。 不是很动态。
  • 在DataGrid上设置ScrollViewer.HorizontalScrollBarVisibility="Disabled"。 当列不适合时效果不佳。
6个回答

16

这里的答案感觉像是一个变通方法,所以我做了一些研究并在Telerik论坛上找到了解决方案,因为我们使用他们的RadGridView。结果发现该解决方案也适用于DataGrid。

关键是将ScrollViewer.HorizontalScrollBarVisibility属性设置为Disabled,如下例所示。

<DataGrid ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<DataGrid.RowDetailsTemplate>
    <DataTemplate>
        <Border>
            <TextBlock Foreground="White" Text="{Binding RowDetails}"
                       TextWrapping="Wrap"/>
        </Border>
    </DataTemplate>
</DataGrid.RowDetailsTemplate>
编辑: 一个副作用是,如果列需要水平方向比可用空间更多的空间,则它们将被裁剪。因此,如果这是一个问题,那么这个解决方案并不是最佳选择。


1
当您实际需要水平滚动条以滚动到隐藏列时,此解决方案无法扩展! - Ahmad

15

这就是我最终采取的方法。虽然我更愿意在DataGrid上使用属性,但由于没有这样的属性,我需要一个解决方法。

alt文本

首先,我只是从父DataGrid中使用ActualWidth,并删除了常量9。这起初很有效,但当垂直滚动条变为可见时,它失败了,所以我不得不使用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>

在转换器中,我使用了另一个常数(16)来补偿可见的垂直滚动条(如果它是可见的)。

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

更新

我对解决方案进行了一些改进,使用ItemsPresenter的ActualWidth而不是DataGrid的ActualWidth(后者不会随着可见的滚动条改变而改变),从而消除了需要多个转换器和两个常量的需要。

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

使用您更新的版本,以ItemsPresenter作为祖先元素,垂直滚动条无法正常工作。我减去了22以避免水平滚动条的出现。 - user2452157
您可以使用 Margin="-3,0" BorderThickness="3,0" 的组合来完全避免转换器: <Border Width="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType={x:Type ItemsPresenter}}}" Margin="-3,0" BorderThickness="3,0"> - Yaakov Shoham

6

我最终采用的方法是将行详细信息的宽度绑定到其显示器的实际宽度,然后添加一个具有不同厚度的边框来补偿显示器中垂直滚动条的存在/缺失。这种方法对我非常有效。示例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>

2
感谢Meleak,你的解决方案对我很有用。这里为WPF新手补充一点小细节:确保将转换器类声明为资源,以便在绑定表达式中引用它。
我将我的转换器放在App.Xaml中,如下所示:
<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>

谢谢您的回复,我将其添加到我的更新解决方案中,这样就不需要使用MultiConverter了。 - Fredrik Hedblad

2

你可以将 MaxWidth 绑定到 ElementName=PART_ColumnHeadersPresenter, Path=ActualWidth 或者 RenderSize.Width。我相信这是 DataGrid 模板中显示列的部分,理论上应该可以工作。


谢谢你的建议!这非常接近我最终所做的事情。 - Fredrik Hedblad

1

为了节省其他人的时间和精力,以下是一些提示:

在与Fredrik Hedblad最新(1/1/11)solution 斗争了一段时间后,我发现ConverterParameter应该是 6 + [左边缘] + [右边缘],其中[左边缘]和[右边缘]是模板中最外层容器的边距。通过查看屏幕截图的放大图,我想这里的6是每行左侧垂直条的宽度。


这个解决方案能否适用于不同的 DPI 设置! - Ahmad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接