Как заставить scrollviewer работать с установкой высоты в Auto в WPF?

Я узнал, что если высота строки сетки, где находится ScrollViewer, установлена ​​как Auto, вертикальная полоса прокрутки не будет действовать, так как фактический размер ScrollViewer может быть больше, чем высота в поле зрения. Поэтому, чтобы заставить полосу прокрутки работать, я должен установить высоту как фиксированное число, так и высоту звезды

Однако теперь у меня есть это требование, что у меня есть два разных представления, которые находятся в двух строках сетки, и у меня есть кнопка переключения между этими двумя представлениями: когда отображается один вид, другой скрывается/исчезает. Поэтому я определил две строки, обе высоты установлены как Auto. И я привязываю видимость представления в каждой строке к логическому свойству из моего ViewModel (один из них преобразуется из True в Visible, а другой из True в Collapsed. Идея заключается в том, Collapsed, высота строки/вида сетки будет автоматически изменена на 0.

Показать/скрывать представление работает нормально. Однако в одном представлении у меня есть ScrollViewer, который, как я упоминал, не работает, когда высота строки задана как Auto. Может ли кто-нибудь сказать мне, как я могу выполнить такое требование, но при этом ScrollViewer работает автоматически? Я думаю, я могу установить высоту кода. Но поскольку я использую MVVM, для этого потребуется дополнительная связь/уведомление. Есть ли более простой способ сделать это?

Ответ 1

Измените высоту от Auto до *, если сможете.

Пример:

    <Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="525">
    <StackPanel Orientation="Horizontal"  Background="LightGray">

        <Grid Width="100">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <ScrollViewer VerticalScrollBarVisibility="Auto" x:Name="_scroll1">
                <Border Height="300" Background="Red" />
            </ScrollViewer>
            <TextBlock Text="{Binding ElementName=_scroll1, Path=ActualHeight}" Grid.Row="1"/>
        </Grid>

        <Grid Width="100">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
                <ScrollViewer VerticalScrollBarVisibility="Auto" x:Name="_scroll2">
                    <Border Height="300" Background="Green" />
                </ScrollViewer>
            <TextBlock Text="{Binding ElementName=_scroll2, Path=ActualHeight}" Grid.Row="1"/>
        </Grid>
    </StackPanel>
</Window>

Ответ 2

В MVVM способ, который работал для меня, заключался в ScrollViewer высоты ScrollViewer к ActualHeight родительского ActualHeight управления (который всегда имеет тип UIElement).

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

<StackPanel>
    <ScrollViewer Height="{Binding Path=ActualHeight, 
           RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UIElement}}">
        <TextBlock Text=Hello"/>
    </ScrollViewer>
</StackPanel>

Но что, если родительский элемент управления имеет бесконечную высоту?

Если родительский элемент управления имеет бесконечную высоту, тогда у нас есть большая проблема. Мы должны продолжать устанавливать высоту всех родителей, пока не достигнем элемента управления с бесконечной высотой.

Snoop абсолютно бесценен для этого:

enter image description here

Если "Высота" для любого элемента XAML равна 0 или NaN, вы можете установить его на что-то, используя одно из:

  • Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UIElement}}"
  • VerticalAlignment="Stretch"
  • Height="Auto"

Подсказка: используйте VerticalAlignment="Stretch" если вы являетесь дочерним элементом Grid с <RowDefinition Height="*"> и Binding RelativeSource... другом месте, если это не работает.


Если вам интересно, вот все мои предыдущие попытки решить эту проблему:

Приложение A: Предыдущая попытка 1

Можно также использовать это:

Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}}"

Приложение Б. Предыдущая попытка 2

Полезная информация: см. WPF - Автоматическая высота в сочетании с MaxHeight.

Если кажется, что ничего не работает, возможно, это потому, что ActualHeight родительского ActualHeight равен либо 0 (поэтому ничего не видно), либо огромному (поэтому никогда не должно появляться средство просмотра прокрутки). Это большая проблема, если есть глубоко вложенные сетки, с прокруткой справа внизу.

  • Используйте Snoop, чтобы найти ActualHeight родительской StackPanel. В свойствах отфильтруйте по слову "Actual", которое возвращает ActualHeight и ActualWidth.
  • Если ActualHeight равен нулю, ActualHeight ему минимальную высоту, используя MinHeight, чтобы мы могли хотя бы что-то увидеть.
  • Если ActualHeight настолько ActualHeight, что выходит за край экрана (т. MaxHeight 16 000), дайте ему разумную максимальную высоту, используя MaxHeight, чтобы появились полосы прокрутки.

Как только появятся полосы прокрутки, мы можем очистить их:

  • Привязать Height StackPanel или Grid к ActualHeight родительского ActualHeight.

Наконец, поместите ScrollViewer внутри этой StackPanel.

Приложение C: Предыдущая попытка 3

Оказывается, иногда это может не сработать:

Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}}"

Причина? Если привязка не удалась, высота будет равна нулю и ничего не будет видно. Привязка может потерпеть неудачу, если мы привязываемся к элементу, который недоступен. Связывание потерпит неудачу, если мы собираемся up визуальное дереве, а затем down к листовому узлу (например, до родительской сетки, затем вниз к ActualHeight из строки, прикрепленной к этой сетке). Вот почему привязка к ActualWidth a RowDefinition просто не будет работать.

Приложение D: Предыдущая попытка 4

Я закончил тем, что начал работать, убедившись, что Height=Auto для всех родительских элементов от нас до первого элемента <Grid> в UserControl.

Ответ 3

Вы можете установить высоту исправления на свой ScrollViewer, но тогда вы должны учитывать, что вторая строка вашей сетки будет иметь такую ​​же высоту, так как первый первый ребенок будет ScrollViewer, а высота строки - автоматически, или вы привязываете высоту ScrollViewer для другого элемента управления в вашем макете. Мы не знаем, как выглядит ваш макет.

В конце, если вам не нравится, что ни один из них не просто устанавливает высоту строки в *, как предложил swiszcz, или взломать wpf, напишите свою собственную панель, которая будет в состоянии компоновать все возможное в каждой параллельной юниверсе или что-то в этом роде.:)

Ответ 4

Я обнаружил, что вы должны поместить ScrollViewer в контейнер с ScrollViewer Height=Auto или получить его родительский Heigh Actual Size и применить его к этому контейнеру.

В моем случае у меня есть UserControl как

  <Grid Margin="0,0,0,0" Padding="0,2,0,0">
                    <ScrollViewer Height="Auto" ZoomMode="Disabled" IsVerticalScrollChainingEnabled="True"  VerticalAlignment="Top"
                     HorizontalScrollMode="Enabled" HorizontalScrollBarVisibility="Disabled"
                     VerticalScrollMode="Enabled" VerticalScrollBarVisibility="Visible"> 
                        <ListView  ItemsSource="{x:Bind PersonalDB.View, Mode=OneWay}" x:Name="DeviceList"
                           ScrollViewer.VerticalScrollBarVisibility="Hidden" 
                    ItemTemplate="{StaticResource ContactListViewTemplate}"
                    SelectionMode="Single"
                    ShowsScrollingPlaceholders="False"
                    Grid.Row="1" 
                    Grid.ColumnSpan="2"
                    VerticalAlignment="Stretch"
                    BorderThickness="0,0,0,0"
                    BorderBrush="DimGray">
                            <ListView.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <ItemsStackPanel AreStickyGroupHeadersEnabled="False" />
                                </ItemsPanelTemplate>
                            </ListView.ItemsPanel>
                            <ListView.GroupStyle>
                                <GroupStyle>
                                    <GroupStyle.HeaderTemplate>
                                        <DataTemplate x:DataType="local1:GroupInfoList">
                                            <TextBlock Text="{x:Bind Key}" 
                                       Style="{ThemeResource TitleTextBlockStyle}"/>
                                        </DataTemplate>
                                    </GroupStyle.HeaderTemplate>
                                </GroupStyle>
                            </ListView.GroupStyle>
                        </ListView>
                    </ScrollViewer>
                </Grid> 

И я добавляю это динамически в ContentControl который находится на Page.

 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="0,0,12,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="70"  /> 
            <RowDefinition Height="*" MinHeight="200"  />
        </Grid.RowDefinitions> 
 <Grid Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"     >
            <ContentControl x:Name="UIControlContainer"  />
        </Grid>
    </Grid>

Обратите внимание, что Heigh Row *

Когда я заполняю ContentControl я использую этот код в событии Loaded

  UIControlContainer.Content = new UIDeviceSelection() { 
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch,
Height = UIControlContainer.ActualHeight,
Width = UIControlContainer.ActualWidth
};

А также, когда ContentControl меняет свой размер, вы должны обновить размер UserControl.

 UIControlContainer.SizeChanged += UIControlContainer_SizeChanged;

 private void UIControlContainer_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (UIControlContainer.Content != null)
            {
                if (UIControlContainer.Content is UserControl)
                {
                    (UIControlContainer.Content as UserControl).Height = UIControlContainer.ActualHeight;
                    (UIControlContainer.Content as UserControl).Width = UIControlContainer.ActualWidth;
                }
            }
        }

Наслаждайтесь!

PS Собственно я это сделал для UWP.