Как заставить 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.