J'ai appris que si la hauteur d'une ligne de la grille, où réside la ScrollViewer
, est définie sur Auto
, la barre de défilement verticale ne prendra pas effet, car la taille réelle de la ScrollViewer
peut être supérieure à la hauteur visible. Donc, afin de faire fonctionner la barre de défilement, je dois définir la hauteur soit sur un nombre fixe, soit sur la hauteur de l'étoile.
Cependant, j'ai maintenant cette exigence, à savoir que deux vues différentes résident dans deux lignes de grille et que je dispose d'un bouton bascule pour basculer entre ces deux vues: lorsqu'une vue est affichée, l'autre est masquée/disparaît. J'ai donc défini deux lignes, les deux hauteurs sont définies comme suit: Auto
. Et je lie la visibilité de la vue dans chaque ligne à une propriété booléenne de mon ViewModel (l'une est convertie de True
à Visible
et l'autre de True
à Collapsed
. L'idée est de savoir si la visibilité d'une vue est Collapsed
/view sera changé en 0 automatiquement.
La vue show/hidden fonctionne bien. Cependant, dans une vue, j'ai une ScrollViewer
, qui, comme je l'ai mentionné, ne fonctionne pas lorsque la hauteur de la ligne est définie sur Auto
. Quelqu'un peut-il me dire comment satisfaire à cette exigence tout en maintenant la ScrollViewer
automatiquement »? Je suppose que je peux définir la hauteur en code-behind. Mais comme j'utilise MVVM, cela nécessiterait une communication/notification supplémentaire. Y a-t-il un moyen plus simple de faire cela?
Changez la hauteur de Auto
à *
, si vous le pouvez.
Exemple:
<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>
Dans MVVM, la méthode qui a fonctionné pour moi a été de lier la hauteur de la variable ScrollViewer
à la variable ActualHeight
du contrôle parent (qui est toujours de type UIElement
).
ActualHeight
est une propriété en lecture seule qui est définie uniquement après que le contrôle a été dessiné à l'écran. Cela peut changer si la fenêtre est redimensionnée.
<StackPanel>
<ScrollViewer Height="{Binding Path=ActualHeight,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UIElement}}">
<TextBlock Text=Hello"/>
</ScrollViewer>
</StackPanel>
Si le contrôle parent a une hauteur infinie, nous avons un problème plus important. Nous devons continuer à régler la taille de tous les parents jusqu'à ce que nous atteignions un contrôle avec une hauteur non infinie.
Snoop est absolument précieux pour cela:
Si la "Hauteur" de l'un des éléments XAML est 0
ou NaN
, vous pouvez le définir sur l'une des options suivantes:
Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UIElement}}"
VerticalAlignment="Stretch"
Height="Auto"
Astuce: Utilisez VerticalAlignment="Stretch"
si vous êtes l'enfant d'une Grid
avec un <RowDefinition Height="*">
et le Binding RelativeSource...
ailleurs si cela ne fonctionne pas.
Si vous êtes intéressé, voici toutes mes tentatives précédentes pour résoudre ce problème:
Peut aussi utiliser ceci:
Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}}"
Informations utiles: voir WPF - Hauteur auto en combinaison avec MaxHeight .
Si rien ne semble fonctionner, c'est probablement parce que la ActualHeight
du parent est 0 (donc rien n'est visible) ou énorme (le visualiseur de défilement n'a donc jamais besoin d'apparaître). C'est plus un problème s'il y a des grilles profondément imbriquées, avec un visionneur de défilement juste en bas.
ActualHeight
du parent StackPanel
. Dans les propriétés, filtrez avec le Word "Actual"
, ce qui ramène ActualHeight
et ActualWidth
.ActualHeight
est égal à zéro, donnez-lui une hauteur minimale en utilisant MinHeight
, afin que nous puissions au moins voir quelque chose.ActualHeight
est si énorme qu’il se détache du bord de l’écran (16 000), attribuez-lui une hauteur maximale raisonnable à l’aide de MaxHeight
pour que les barres de défilement apparaissent.Une fois que les barres de défilement apparaissent, nous pouvons alors les nettoyer:
Height
de la StackPanel
ou la Grid
à la ActualHeight
du parent.Enfin, mettez une ScrollViewer
dans cette StackPanel
.
Il se trouve que cela peut parfois échouer:
Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}}"
La raison? Si la liaison échoue, la hauteur sera nulle et rien ne sera vu. La liaison peut échouer si nous lions un élément non accessible. La liaison échouera si nous allons up
l'arborescence visuelle, puis down
à un nœud feuille (par exemple, jusqu'à la grille parente, puis jusqu'à la ActualHeight
d'une ligne attachée à cette grille). C'est pourquoi la liaison à la ActualWidth
d'une RowDefinition
ne fonctionnera tout simplement pas.
J'ai finalement réussi à faire fonctionner cela en m'assurant que Height=Auto
pour tous les éléments parents de notre part correspondait au premier élément <Grid>
du contrôle UserControl.
Vous pouvez soit définir une hauteur fixe sur votre ScrollViewer, mais vous devez également considérer que la deuxième ligne de votre grille aura aussi cette hauteur puisque le premier enfant de la ligne sera ScrollViewer et que la hauteur de la ligne est auto, ou vous liez la hauteur à ScrollViewer. un autre contrôle dans votre mise en page. Nous ne savons pas à quoi ressemble votre mise en page.
À la fin, si vous n'aimez ni l'un ni l'autre, réglez la hauteur de la ligne sur * comme suggéré par swiszcz ou hackez wpf et écrivez votre propre panneau personnalisé qui permettra de mettre en page tout ce qui est possible dans chaque univers parallèle ou quelque chose comme ça. :)
Ce que je découvre, c'est que vous devez placer votre ScrollViewer
dans un conteneur qui a le Height=Auto
ou vous obtenez son parent Heigh Actual Size
et l'appliquer à ce conteneur.
Dans mon cas, j'ai UserControl
comme
<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>
Et je l’ajoute dinamiquement à ContentControl
qui est dans un 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>
Notez que Heigh
de la Row
est *
Lorsque je remplis ContentControl
j'utilise ce code dans l'événement Loaded
UIControlContainer.Content = new UIDeviceSelection() {
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch,
Height = UIControlContainer.ActualHeight,
Width = UIControlContainer.ActualWidth
};
Et aussi lorsque ContentControl
change sa taille, vous devez mettre à jour la taille de 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;
}
}
}
Prendre plaisir!
P.S. En fait, je l'ai fait pour UWP.