Nested Scroll Areas

前端 未结 7 539
失恋的感觉
失恋的感觉 2020-12-05 15:34

I creating a control for WPF, and I have a question for you WPF gurus out there.

I want my control to be able to expand to fit a resizable window.

In my con

相关标签:
7条回答
  • 2020-12-05 16:08

    I've created a class to work around this problem:

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

    It will always return a desired size of (0,0), even if the containing element wants to be bigger. Usage:

    <local:RestrictDesiredSize MinWidth="200" MinHeight="200">
         <ListBox />
    </local>
    
    0 讨论(0)
  • 2020-12-05 16:14

    Create a method in the code-behind that sets the ListBox's MaxHeight to the height of whatever control is containing it and other controls. If the Listbox has any controls/margins/padding above or below it, subtract their heights from the container height assigned to MaxHeight. Call this method in the main windows "loaded" and "window resize" event handlers.

    This should give you the best of both worlds. You are giving the ListBox a "fixed" size that will cause it to scroll in spite of the fact that the main window has its own scrollbar.

    0 讨论(0)
  • 2020-12-05 16:23

    You problem arises, because Controls within a ScrollViewer have virtually unlimited space available. Therefore your inner ListBox thinks it can avoid scrolling by taking up the complete height necessary to display all its elements. Of course in your case that behaviour has the unwanted side effect of exercising the outer ScrollViewer too much.

    The objective therefore is to get the ListBox to use the visible height within the ScrollViewer iff there is enough of it and a certain minimal height otherwise. To achieve this, the most direct way is to inherit from ScrollViewer and override MeasureOverride() to pass an appropriately sized availableSize (that is the given availableSize blown up to the minimal size instead of the "usual" infinity) to the Visuals found by using VisualChildrenCount and GetVisualChild(int).

    0 讨论(0)
  • 2020-12-05 16:23

    I used Daniels solution. That works great. Thank you.

    Then I added two boolean dependency properties to the decorator class: KeepWidth and KeepHeight. So the new feature can be suppressed for one dimension.

    This requires a change in 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);
    }
    
    0 讨论(0)
  • 2020-12-05 16:24

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

    code:

      <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>
    
    0 讨论(0)
  • 2020-12-05 16:27

    While I wouldn't recommend creating a UI that requires outer scroll bars you can accomplish this pretty easily:

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

    I don't really recommend this. WPF provides exceptional layout systems, like Grid, and you should try to allow the app to resize itself as needed. Perhaps you can set a MinWidth/MinHeight on the window itself to prevent this resizing?

    0 讨论(0)
提交回复
热议问题