it-swarm.com.de

Wie funktioniert der Scrollviewer, wenn die Höhe in WPF auf Auto eingestellt ist?

Ich habe erfahren, dass, wenn die Höhe einer Rasterzeile, in der sich die ScrollViewer befindet, als Auto festgelegt ist, die vertikale Bildlaufleiste nicht wirksam wird, da die tatsächliche Größe der ScrollViewer größer sein kann als die sichtbare Höhe. Damit die Bildlaufleiste funktioniert, sollte die Höhe entweder auf eine feste Zahl oder auf eine Sternhöhe eingestellt werden

Jetzt habe ich jedoch die Anforderung, dass zwei verschiedene Ansichten in zwei Rasterzeilen gespeichert sind und ich über eine Umschalttaste zwischen diesen beiden Ansichten wechseln kann: Wenn eine Ansicht angezeigt wird, wird die andere ausgeblendet/ausgeblendet. Also habe ich zwei Zeilen definiert, beide Höhen sind als Auto gesetzt. Und ich binde die Sichtbarkeit der Ansicht in jeder Zeile an eine boolesche Eigenschaft aus meinem ViewModel (eine wird von True in Visible und die andere von True in Collapsed konvertiert. Die Idee ist, wenn die Sichtbarkeit einer Ansicht Collapsed ist, die Höhe der Rasterzeile/view wird automatisch auf 0 gesetzt.

Die Ansicht show/hidden funktioniert einwandfrei. In einer Ansicht habe ich jedoch eine ScrollViewer, die, wie bereits erwähnt, nicht funktioniert, wenn die Zeilenhöhe auf Auto eingestellt ist. Kann mir jemand sagen, wie ich eine solche Anforderung erfüllen kann, während die ScrollViewer noch automatisch arbeitet? Ich denke, ich kann die Höhe in Code-Behind einstellen. Da ich jedoch MVVM verwende, ist eine zusätzliche Kommunikation/Benachrichtigung erforderlich. Gibt es einen einfacheren Weg, das zu tun?

12
tete

Ändern Sie die Höhe von Auto in *, wenn Sie können.

Beispiel:

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

In MVVM funktionierte für mich die Methode, die Höhe der Variable ScrollViewer an die Variable ActualHeight des übergeordneten Steuerelements (die immer vom Typ UIElement ist) zu binden.

ActualHeight ist eine schreibgeschützte Eigenschaft, die erst festgelegt wird, nachdem das Steuerelement auf den Bildschirm gezeichnet wurde. Es kann sich ändern, wenn die Größe des Fensters geändert wird. 

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

Was aber, wenn das übergeordnete Steuerelement eine unendliche Höhe hat?

Wenn das übergeordnete Steuerelement eine unendliche Höhe hat, haben wir ein größeres Problem. Wir müssen immer die Höhe aller Eltern einstellen, bis wir eine Kontrolle mit einer unendlichen Höhe treffen.

Snoop ist dafür von unschätzbarem Wert:

 enter image description here

Wenn die "Höhe" für ein XAML-Element 0 oder NaN ist, können Sie einen Wert mit einem der folgenden Werte festlegen:

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

Hinweis: Verwenden Sie VerticalAlignment="Stretch", wenn Sie einer Grid mit einem <RowDefinition Height="*"> untergeordnet sind, und Binding RelativeSource... woanders, falls dies nicht funktioniert.


Wenn Sie interessiert sind, finden Sie hier alle meine bisherigen Versuche, dieses Problem zu beheben:

Anhang A: Vorheriger Versuch 1

Kann auch dies verwenden:

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

Anhang B: Vorheriger Versuch 2

Nützliche Informationen: siehe WPF - Auto Height in Kombination mit MaxHeight

Wenn nichts zu funktionieren scheint, liegt dies wahrscheinlich daran, dass die ActualHeight des übergeordneten Elements entweder 0 ist (also nichts sichtbar ist) oder riesig ist (der Scrollviewer muss also nicht angezeigt werden). Dies ist eher ein Problem, wenn tief verschachtelte Gitter vorhanden sind und sich unten rechts ein Scrollviewer befindet.

  • Verwenden Sie Snoop, um die ActualHeight der übergeordneten StackPanel zu suchen. Filtern Sie in den Eigenschaften nach dem Wort "Actual", wodurch ActualHeight und ActualWidth zurückgegeben werden.
  • Wenn ActualHeight Null ist, geben Sie mit MinHeight eine Mindesthöhe an, damit wir zumindest etwas sehen können.
  • Wenn ActualHeight so groß ist, dass sie sich vom Rand des Bildschirms entfernt (d. H. 16.000), legen Sie mit MaxHeight eine angemessene maximale Höhe fest, sodass die Bildlaufleisten angezeigt werden.

Sobald die Bildlaufleisten angezeigt werden, können wir sie weiter bereinigen:

  • Binden Sie die Height der StackPanel oder Grid an die ActualHeight des übergeordneten Elements.

Fügen Sie schließlich eine ScrollViewer in diese StackPanel ein.

Anhang C: Vorheriger Versuch 3

Es stellt sich heraus, dass dies manchmal fehlschlagen kann:

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

Der Grund? Wenn die Bindung fehlschlägt, wird die Höhe Null und nichts wird gesehen. Die Bindung kann fehlschlagen, wenn wir an ein Element binden, das nicht zugänglich ist. Die Bindung schlägt fehl, wenn wir up den visuellen Baum und dann down zu einem Blattknoten (z. B. bis zum übergeordneten Raster und dann bis zu ActualHeight einer mit diesem Gitter verbundenen Reihe) wechseln. Deshalb funktioniert das Binden an die ActualWidth einer RowDefinition einfach nicht.

Anhang D: Vorheriger Versuch 4

Am Ende habe ich das funktioniert, indem sichergestellt wurde, dass Height=Auto für alle übergeordneten Elemente von uns bis zum ersten <Grid>-Element im UserControl.

20
Contango

Sie können entweder eine feste Höhe in Ihrem ScrollViewer festlegen, aber dann müssen Sie berücksichtigen, dass auch die zweite Zeile Ihres Rasters diese Höhe hat, da das erste untergeordnete Element der Zeile der ScrollViewer ist und die Zeilenhöhe automatisch ist, oder Sie binden die Höhe des ScrollViewer an ein anderes Steuerelement in Ihrem Layout. Wir wissen nicht, wie Ihr Layout gleich aussieht.

Wenn Sie keine der beiden Möglichkeiten mögen, setzen Sie einfach die Zeilenhöhe auf *, wie von swiszcz vorgeschlagen, oder hacken Sie wpf ein eigenes benutzerdefiniertes Panel, das in der Lage ist, alles Mögliche in jedem Paralleluniversum oder ähnliches zu gestalten. :)

0
dev hedgehog

Ich stelle fest, dass Sie Ihre ScrollViewer in einen Container mit Height=Auto setzen müssen, oder Sie erhalten seinen übergeordneten Heigh Actual Size und wenden ihn auf diesen Container an.

In meinem Fall habe ich UserControl gefällt

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

Und ich füge es dinamisch zu ContentControl hinzu, das sich innerhalb einer Page befindet.

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

Beachten Sie, dass Heigh der Row* ist.

Wenn ich ContentControl auffülle, verwende ich diesen Code im Loaded-Ereignis

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

Und auch wenn ContentControl seine Größe ändert, müssen Sie die Größe der UserControl aktualisieren.

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

Genießen!

P.S. Eigentlich habe ich es für UWP gemacht.