web-dev-qa-db-ja.com

WPFで高さを自動に設定してscrollviewerを機能させるにはどうすればよいですか?

ScrollViewerが存在するグリッド行の高さがAutoに設定されている場合、ScrollViewerは、見えている高さよりも大きくすることができます。スクロールバーを機能させるには、高さを固定数または星の高さに設定する必要があります

ただし、この要件があります。2つのグリッド行に2つの異なるビューがあり、これらの2つのビューを切り替えるトグルボタンがあります。1つのビューが表示されると、もう1つのビューは非表示/非表示になります。したがって、2つの行を定義しました。どちらの高さもAutoとして設定されています。そして、各行のビューの可視性をViewModelのブールプロパティにバインドします(1つはTrueからVisibleに、もう1つはTrueからCollapsed。アイデアは、1つのビューの可視性がCollapsedの場合、グリッドの行/ビューの高さが自動的に0に変更されることです。

表示/非表示のビューは正常に機能しています。ただし、1つのビューにはScrollViewerがあり、前述のとおり、行の高さがAutoに設定されている場合は機能しません。 ScrollViewerを自動的に機能させながら、このような要件を満たす方法を誰かに教えてもらえますか?高さを分離コードで設定できると思います。しかし、MVVMを使用しているため、追加の通信/通知が必要になります。それを行う簡単な方法はありますか?

16
tete

可能であれば、高さを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>
15

MVVMでは、ScrollViewerの高さを親コントロール(常にActualHeight型)のUIElementにバインドするのがうまくいきました。

ActualHeightは、コントロールが画面に描画された後にのみ設定される読み取り専用のプロパティです。ウィンドウのサイズが変更されると、変更される場合があります。

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

しかし、親コントロールの高さが無限の場合はどうなるでしょうか。

親コントロールの高さが無限である場合、より大きな問題が発生します。高さが無限でないコントロールにぶつかるまで、すべての親の高さを設定し続ける必要があります。

スヌープはこれのために絶対に貴重です:

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

付録B:以前の試み2

役立つ情報: WPF-MaxHeightと組み合わせたAuto Height を参照してください。

何も動作しないように見える場合は、親のActualHeightが0(何も表示されていない)または巨大(スクロールビューアが表示される必要がない)のいずれかが原因であると考えられます。グリッドのネストが深く、スクロールビューアが右下にある場合、これはさらに問題になります。

  • Snoopを使用して、親ActualHeightStackPanelを見つけます。プロパティで、ActualHeightActualWidthを戻すWord "Actual"でフィルタリングします。
  • ActualHeightがゼロの場合は、MinHeightを使用して最小の高さを与え、少なくとも何かが見えるようにします。
  • ActualHeightが大きすぎて画面の端からはみ出る場合(つまり16,000)、MaxHeightを使用して適切な最大の高さを指定すると、スクロールバーが表示されます。

スクロールバーが表示されたら、さらにクリーンアップできます。

  • HeightまたはStackPanelGridを親のActualHeightにバインドします。

最後に、ScrollViewerをこのStackPanelの中に入れます。

付録C:以前の試み3

これは時々失敗する可能性があることがわかります:

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

理由?バインドが失敗すると、高さがゼロになり、何も表示されなくなります。アクセスできない要素にバインドしている場合、バインドは失敗する可能性があります。ビジュアルツリーをupに移動し、次にリーフノードにdownした場合(たとえば、親グリッドまで、行のActualHeightまでをバインドすると、バインドは失敗します。そのグリッドに接続されています)。これが、ActualWidthRowDefinitionへのバインドが機能しない理由です。

付録D:以前の試み4

私は、UserControlの最初のHeight=Auto要素までのすべての親要素の<Grid>を確認することで、これを機能させることになりました。

26
Contango

ScrollViewerに固定の高さを設定できますが、行の最初の子がScrollViewerになり、行の高さが自動になるため、グリッドの2番目の行にもその高さがあることを考慮する必要があります。または、ScrollViewerの高さをレイアウト内の別のコントロール。あなたのレイアウトがどのように見えるかわかりません。

最後に、どちらも好きではない場合は、swiszczが提案するように行の高さを*に設定するか、wpfをハックして、すべてのパラレルユニバースなどで可能なすべてをレイアウトできる独自のカスタムパネルを作成します。 :)

0
dev hedgehog

私が発見したのは、ScrollViewerHeight=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>

HeighRow*

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

楽しい!

追伸実際、私はUWPのためにそれをしました。

0
Developer