c# - How to fix headers both left and top in a grid with scrollviewers - Stack Overflow

I am using wpf and I got a pretty big table, which needs to be scrollable both horizontaly, as well as

I am using wpf and I got a pretty big table, which needs to be scrollable both horizontaly, as well as vertically. It also has headers at the top and the left. These need be fixed, because the table is way to big scroll to the header all the time.

I tried using an approach with grids and scrollviews like these Fixed header and footer outside scrollview, . This lead to my current structure:

<Scrollviewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible">
    <Grid>
        <!-- Here is the left Header
    </Grid>
    <Scrollviewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled">
        <Grid>
            <!-- Rest of the table -->
        </Grid>
    </Scrollviewer>
</Scrollviewer>

However this seems to be intended only for applications which scroll only in one dircetion. But in my case I have 2 problems with this approach.

  1. The horizontal scrollview is not fixed to the bottom of the page. If you want to see the bar you have to scroll all the way to the bottom first.
  2. Currently only the header at the left is fixed, and i got no idea how to fix the one on the top. If I were to exclude this part of the grid again and put it outside everything else, then the top header would be outside the horizontal scollviewer. There is no way getting both headers into both scrollviewers AND fix them to the place.

Does anyone have an idea how to solve this. Idealy I could define the position of scrollviewer seperate from its content. In that case I could make one Grid, with both headers, the body of the table and the scrollviews, and then I could set the content the scrollviews so it scroll to the body of the table.

EDIT: Fot one Grid in the code. Here is the corrected Version.

<Scrollviewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible">
    <Grid>
        <Grid>
            <!-- Here is the left Header -->
        </Grid>
        <Scrollviewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled">
            <Grid>
                <!-- Rest of the table -->
            </Grid>
        </Scrollviewer>
    </Grid>
</Scrollviewer>

Also to make it clear here is what I want

with green being the horizintal scrollbar and blue the vertical. Both should always be visible. Currently I only see the green one if I sroll all the way down.

The headers (Rwo x, Column x) need to be always visible. On the same time they also have to be included in the scrollbar. So on the left are always 10 rows visible, but if I scrolldown I see 12-22 for example.

I am using wpf and I got a pretty big table, which needs to be scrollable both horizontaly, as well as vertically. It also has headers at the top and the left. These need be fixed, because the table is way to big scroll to the header all the time.

I tried using an approach with grids and scrollviews like these Fixed header and footer outside scrollview, https://learn.microsoft/en-us/answers/questions/537119/how-to-freeze-grid-column-in-scrollviewer-or-achie. This lead to my current structure:

<Scrollviewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible">
    <Grid>
        <!-- Here is the left Header
    </Grid>
    <Scrollviewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled">
        <Grid>
            <!-- Rest of the table -->
        </Grid>
    </Scrollviewer>
</Scrollviewer>

However this seems to be intended only for applications which scroll only in one dircetion. But in my case I have 2 problems with this approach.

  1. The horizontal scrollview is not fixed to the bottom of the page. If you want to see the bar you have to scroll all the way to the bottom first.
  2. Currently only the header at the left is fixed, and i got no idea how to fix the one on the top. If I were to exclude this part of the grid again and put it outside everything else, then the top header would be outside the horizontal scollviewer. There is no way getting both headers into both scrollviewers AND fix them to the place.

Does anyone have an idea how to solve this. Idealy I could define the position of scrollviewer seperate from its content. In that case I could make one Grid, with both headers, the body of the table and the scrollviews, and then I could set the content the scrollviews so it scroll to the body of the table.

EDIT: Fot one Grid in the code. Here is the corrected Version.

<Scrollviewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible">
    <Grid>
        <Grid>
            <!-- Here is the left Header -->
        </Grid>
        <Scrollviewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled">
            <Grid>
                <!-- Rest of the table -->
            </Grid>
        </Scrollviewer>
    </Grid>
</Scrollviewer>

Also to make it clear here is what I want

with green being the horizintal scrollbar and blue the vertical. Both should always be visible. Currently I only see the green one if I sroll all the way down.

The headers (Rwo x, Column x) need to be always visible. On the same time they also have to be included in the scrollbar. So on the left are always 10 rows visible, but if I scrolldown I see 12-22 for example.

Share Improve this question edited Mar 14 at 8:33 Tobias asked Mar 13 at 16:17 TobiasTobias 3832 silver badges13 bronze badges 4
  • ScrollViewer can only have 1 children, shown xaml is invalid. Without some screenshots it's kind of hard to imagine what you want. "If you want to see the bar you have to scroll all the way to the bottom first" - this is solved by using "floating" scrollbars, i.e. don't use horizontal scrollbar of ScrollViewer, but host additional one on the level of parent scrollviewer, so it will be always accessible, without need to scroll. – Sinatr Commented Mar 13 at 16:48
  • " no idea how to fix the one on the top" - is it "frozen" columns/rows concept from excel? – Sinatr Commented Mar 13 at 16:51
  • 3x3 grid, [0,0] - is nothiing, [0,1] - left columns, [1,0] - top columns, [1,1] - scrollable content, [1,2] - vertical scrollbar, [2,1] - horizontal scrollbar. Scrollbars scroll content and corresponding headers. You need to play with bindings and use at least 3 scrollviewers in this setup. – Sinatr Commented Mar 13 at 16:58
  • @Sinatr 1.Yes the code is invalid. I fot one Grid. I will Edit that one. 2. Yes the "frozen" columns/rows are what I want. 3. That last one sounds like what I want. But I do not understand how the scrollviewer can be in [2,1] while it is responsible for scrolling the content in [1,1] and [0,1] – Tobias Commented Mar 14 at 8:22
Add a comment  | 

1 Answer 1

Reset to default 1

To get you started, I've played a bit with my idea and it worked, but I made it only for horizontal ScrollBar.

You need 3 ScrollViewer (for left/top headers and content) and 2 ScrollBar. I simulate headers and content with some stripped background (to see how it scrolls).

The important part how to use single ScrollBar to scroll content in many ScrollViewers. I went with "attached behavior" solution.

Xaml:


<Grid>
    <Grid.Resources>
        <GradientStopCollection x:Key="strippedGradient" x:Shared="False">
            <GradientStop Offset="0" Color="Black" />
            <GradientStop Offset="0.5" />
        </GradientStopCollection>
    </Grid.Resources>

    <Grid.RowDefinitions>
        <RowDefinition Height="auto" />
        <RowDefinition />
        <RowDefinition Height="auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="auto" />
        <ColumnDefinition />
        <ColumnDefinition Width="auto" />
    </Grid.ColumnDefinitions>

    <!-- left columns -->
    <ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
        <TextBlock Width="50" Height="1000">
            <TextBlock.Background>
                <LinearGradientBrush StartPoint="0,0" EndPoint="0,20" MappingMode="Absolute" SpreadMethod="Repeat" GradientStops="{StaticResource strippedGradient}" />
            </TextBlock.Background>
        </TextBlock>
    </ScrollViewer>

    <!-- top columns -->
    <ScrollViewer Grid.Column="1" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="hidden" local:ScrollViewerBehavior.HorizontalScrollBar="{Binding ElementName=horizontalScrollBar}">
        <TextBlock Width="1000" Height="50">
            <TextBlock.Background>
                <LinearGradientBrush StartPoint="0,0" EndPoint="20,0" MappingMode="Absolute" SpreadMethod="Repeat" GradientStops="{StaticResource strippedGradient}" />
            </TextBlock.Background>
        </TextBlock>
    </ScrollViewer>

    <!-- content in the center -->
    <ScrollViewer Grid.Row="1" Grid.Column="1" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" local:ScrollViewerBehavior.HorizontalScrollBar="{Binding ElementName=horizontalScrollBar}">
        <TextBlock Width="1000" Height="1000">
            <TextBlock.Background>
                <LinearGradientBrush StartPoint="0,0" EndPoint="20,20" MappingMode="Absolute" SpreadMethod="Repeat" GradientStops="{StaticResource strippedGradient}" />
            </TextBlock.Background>
        </TextBlock>
    </ScrollViewer>

    <!-- horizontal scrollbar at bottom -->
    <ScrollBar x:Name="horizontalScrollBar" Grid.Row="2" Grid.Column="1" SmallChange="10" LargeChange="100" Orientation="Horizontal" />

</Grid>

Demo:

Behavior:

public class ScrollViewerBehavior : DependencyObject
{
    public static ScrollBar GetHorizontalScrollBar(DependencyObject obj) =>
        (ScrollBar)obj.GetValue(HorizontalScrollBarProperty);
    public static void SetHorizontalScrollBar(DependencyObject obj, ScrollBar value) =>
        obj.SetValue(HorizontalScrollBarProperty, value);

    public static readonly DependencyProperty HorizontalScrollBarProperty = DependencyProperty.RegisterAttached(
        "HorizontalScrollBar", typeof(ScrollBar), typeof(ScrollViewerBehavior), new PropertyMetadata((sender, args) =>
        {
            if (sender is not ScrollViewer scrollViewer)
                throw new InvalidOperationException("Can only be applied to ScrollViewer");

            // prevent in designer
            if (Application.Current is not App)
                return;

            if (args.NewValue is ScrollBar scrollBar)
            {
                // Value={Binding HorizontalOffset}
                scrollBar.SetBinding(ScrollBar.ValueProperty, new Binding()
                {
                    Path = new(ScrollViewer.HorizontalOffsetProperty),
                    Source = scrollViewer,
                    Mode = BindingMode.OneWay,
                });

                // Maximum={Binding ScrollableWidth}
                scrollBar.SetBinding(ScrollBar.MaximumProperty, new Binding()
                {
                    Path = new(ScrollViewer.ScrollableWidthProperty),
                    Source = scrollViewer,
                    Mode = BindingMode.OneWay,
                });

                // ViewportSize={Binding VieportWidth}
                scrollBar.SetBinding(ScrollBar.ViewportSizeProperty, new Binding()
                {
                    Path = new(ScrollViewer.ViewportWidthProperty),
                    Source = scrollViewer,
                    Mode = BindingMode.OneWay,
                });

                scrollBar.Scroll += (s, e) => scrollViewer.ScrollToHorizontalOffset(scrollBar.Value);
            }
        }));
}

TODO: add vertical scrollbar similarly, fix bugs.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744690827a4588199.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信