wpf 嵌套的滾動區域




layout scroll (5)

為2 ScrollViewer

   public class ScrollExt: ScrollViewer
{
    Size lastArrangeSize = new Size(double.PositiveInfinity, double.PositiveInfinity);

    public ScrollExt()
    {

    }
    protected override Size MeasureOverride(Size constraint)
    {
        base.MeasureOverride(new Size(Math.Min(lastArrangeSize.Width, constraint.Width),
                                      Math.Min(lastArrangeSize.Height, constraint.Height)));
        return new Size(0, 0);
    }

    protected override Size ArrangeOverride(Size arrangeSize)
    {
        if (lastArrangeSize != arrangeSize)
        {
            lastArrangeSize = arrangeSize;
            base.MeasureOverride(arrangeSize);
        }
        return base.ArrangeOverride(arrangeSize);
    }
}

碼:

  <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <Grid >
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <TextBlock Background="Beige" Width="600" Text="Example"/>
            <Grid Grid.Column="1" x:Name="grid">
                    <Grid Grid.Column="1" Margin="25" Background="Green">
                    <local:ScrollExt HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
                        <Grid Width="10000" Margin="25" Background="Red" />
                    </local:ScrollExt>
                    </Grid>
                </Grid>
        </Grid>
    </ScrollViewer>

我創建了一個WPF的控件,我有一個WPF大師的問題在那裡。

我希望我的控件能夠擴展以適應可調整大小的窗口。

在我的控制下,我有一個我想用窗口擴展的列錶框。 我也有列錶框(按鈕,文本等)周圍的其他控件。

我希望能夠在我的控件上設置最小尺寸,但是我希望窗口能夠通過創建滾動條來查看控件的大小。

這將創建嵌套的滾動區域:一個用於列錶框和一個包裝整個控件的ScrollViewer。

現在,如果列錶框設置為自動調整大小,它將永遠不會有滾動條,因為它總是在ScrollViewer中被全尺寸繪製。

我只想控制滾動,如果內容不能變小,否則我不想滾動控制; 相反,我想滾動控件內的列錶框。

我如何改變ScrollViewer類的默認行為? 我嘗試從ScrollViewer類繼承,並重寫MeasureOverride和ArrangeOverride類,但我無法弄清楚如何正確測量和安排孩子。 看起來,該安排必須以某種方式影響ScrollContentPresenter,而不是實際的內容子節點。

任何幫助/建議將不勝感激。


Answer #1

我已經創建了一個類來解決這個問題:

public class RestrictDesiredSize : Decorator
{
    Size lastArrangeSize = new Size(double.PositiveInfinity, double.PositiveInfinity);

    protected override Size MeasureOverride(Size constraint)
    {
        Debug.WriteLine("Measure: " + constraint);
        base.MeasureOverride(new Size(Math.Min(lastArrangeSize.Width, constraint.Width),
                                      Math.Min(lastArrangeSize.Height, constraint.Height)));
        return new Size(0, 0);
    }

    protected override Size ArrangeOverride(Size arrangeSize)
    {
        Debug.WriteLine("Arrange: " + arrangeSize);
        if (lastArrangeSize != arrangeSize) {
            lastArrangeSize = arrangeSize;
            base.MeasureOverride(arrangeSize);
        }
        return base.ArrangeOverride(arrangeSize);
    }
}

它將始終返回(0,0)所需的大小,即使包含元素想要變大。 用法:

<local:RestrictDesiredSize MinWidth="200" MinHeight="200">
     <ListBox />
</local>

Answer #2

雖然我不建議創建一個需要外部滾動條的用戶界面,但您可以非常輕鬆地完成此操作:

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >    
    <ScrollViewer HorizontalScrollBarVisibility="Auto" 
                  VerticalScrollBarVisibility="Auto">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <ListBox Grid.Row="0" Grid.RowSpan="3" Grid.Column="0" MinWidth="200"/>
            <Button Grid.Row="0" Grid.Column="1" Content="Button1"/>
            <Button Grid.Row="1" Grid.Column="1" Content="Button2"/>
            <Button Grid.Row="2" Grid.Column="1" Content="Button3"/>
        </Grid>
    </ScrollViewer>
</Window>

我真的不推薦這個。 WPF提供了非常好的佈局系統,比如Grid,你應該試著讓應用根據需要調整自己的大小。 也許你可以在窗口上設置MinWidth / MinHeight來防止這個調整大小?


Answer #3

我用丹尼爾的解決方案。 這很好。 謝謝。

然後我添加了兩個布爾依賴屬性到裝飾類: KeepWidthKeepHeight 。 所以新功能可以被抑制一維。

這需要更改MeasureOverride

protected override Size MeasureOverride(Size constraint)
{
    var innerWidth = Math.Min(this._lastArrangeSize.Width, constraint.Width);
    var innerHeight = Math.Min(this._lastArrangeSize.Height, constraint.Height);
    base.MeasureOverride(new Size(innerWidth, innerHeight));

    var outerWidth = KeepWidth ? Child.DesiredSize.Width : 0;
    var outerHeight = KeepHeight ? Child.DesiredSize.Height : 0;
    return new Size(outerWidth, outerHeight);
}

Answer #4

您遇到問題,因為ScrollViewer幾乎沒有空間可用。 因此,你的內部ListBox認為它可以通過佔用顯示其所有元素所需的全部高度來避免滾動。 當然在你的情況下,行為有太多的外部ScrollViewer行使有害的副作用。

因此,目標是讓ListBoxScrollViewer使用可見的高度,如果足夠的話還有一定的最小高度。 為了達到這個目的,最直接的方法就是從ScrollViewer繼承並重寫MeasureOverride()來傳遞一個尺寸適當的availableSize (也就是給定的availableSize尺寸放大到最小尺寸,而不是“通常”無窮大)使用VisualChildrenCountGetVisualChild(int)





scroll