Ошибка DataGrid RowDetails Width

Предположим, что у меня есть 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, а не для самой самой ширины?

Вещи, которые я пробовал, которые завершают упаковку, но не удовлетворительным образом

  • Установите ширину или максимальную ширину на границе или в TextBlock. Не очень динамично.
  • Установить ScrollViewer.HorizontalScrollBarVisibility = "Отключено" в DataGrid. Не очень хорошо, когда столбцы не подходят.

Ответ 1

Это то, что я закончил делать. Я предпочел бы использовать свойство для DataGrid для этого, но поскольку такого свойства не существует, мне нужно обходное решение.

alt text

Сначала я просто использовал ActualWidth из родительского DataGrid и удалил константу из 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;
    }
}

Обновить

Я немного улучшил решение, используя ActualWidth для ItemsPresenter, а не DataGrid (где ActualWidth не изменился в зависимости от видимого ScrollBar), тем самым устраняя необходимость в MultiConverter и двух константах.

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

Ответ 2

Ответы здесь были обходными, поэтому я сделал некоторые исследования и нашел решение на форумах 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>

Изменить: Побочным эффектом является то, что если столбцам требуется больше пространства по горизонтали, чем есть место, они будут обрезаны. Поэтому, если это проблема, то это решение не является оптимальным.

Ответ 3

Возможно, вы сможете привязать MaxWidth к ElementName=PART_ColumnHeadersPresenter, Path=ActualWidth или, возможно, к RenderSize.Width. Я считаю, что это часть шаблона DataGrid, которая отображает столбцы, поэтому теоретически она должна работать

Ответ 4

Спасибо Meleak, ваше решение сработало для меня. Одно небольшое дополнение для нас новичков WPF. Обязательно объявите свой класс конвертера ресурсом, чтобы на него можно было ссылаться в выражении Binding.

Я поместил мой в 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>

Ответ 5

Это то, что я закончил: привязка ширины строки строки к фактической ширине своего презентатора, а затем добавила границу с разной толщиной, чтобы компенсировать наличие/отсутствие вертикальной полосы прокрутки в презентаторе. Этот подход отлично работал у меня. Пример 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>

Ответ 6

Чтобы сохранить других людей, некоторые царапины головы и время проб и ошибок:

После суеты с Fredrik Hedblad последнее (1/1/11) решение в течение некоторого времени я понял, что значение ConverterParameter должно 6 + [левое поле} + [правое поле] (т.е. поля внешнего контейнера в шаблоне). После изучения раздутия снимка экрана я ожидаю 6 - ширина вертикальной полосы слева от каждой строки.