gzsun
gzsun

Reputation: 329

How to implement WPF custom grid with scrolling support

I asked a few months ago similarly question, but now I have different problem and I am not sure how to solve that.

Picture below describes what I want to implement - in fact I want DataGrid behavior. With 'fixed' keyword I mean that:

1) Header is always visible when I use vertical scrollbar, and header is moving if I use horizontal scrollbar 2) Rows are always visible when I use horizontal scrollbar, and rows are moving if I use vertical scrollbar

enter image description here

I need to create Grid on dynamically way, because I don't know number of rows or columns in advance.

My current solution doesn't include 'fixed rows' and my vertical scroll bar is not visible always (orange line at picture).

XAML

<Grid>
    <Grid Grid.IsSharedSizeScope="True">

        <ScrollViewer CanContentScroll="True" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled" Grid.Row="2" Grid.ColumnSpan="2">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="35" />
                    <RowDefinition Height="600" />
                </Grid.RowDefinitions>

                <Grid x:Name="HeaderDaysGrid" Margin="10,0,0,0" Grid.Row="0" ShowGridLines="False">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="150" MaxWidth="150"/>
                        <ColumnDefinition Width="*" MaxWidth="240"/>
                        <ColumnDefinition Width="*" MaxWidth="240"/>
                        <ColumnDefinition Width="*" MaxWidth="240"/>
                        <ColumnDefinition Width="*" MaxWidth="240"/>
                        <ColumnDefinition Width="*" MaxWidth="240"/>
                        <ColumnDefinition Width="*" MaxWidth="240"/>
                        <ColumnDefinition Width="*" MaxWidth="240"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="35"/>
                    </Grid.RowDefinitions>
                </Grid>

                <Grid x:Name="SchedulerGridWrapper" Grid.Row="1" Margin="10,0,0,5">
                    <ScrollViewer x:Name="SchedulerScrolViewer" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Height="600">
                        <Grid x:Name="WeekSchedulerGrid" ShowCustomGridLines="False" ScrollViewer.CanContentScroll="True" />
                    </ScrollViewer>
                </Grid>
            </Grid>
        </ScrollViewer>
    </Grid>
</Grid>

And in code behind I have something like this:

private void CreateWeekGrid()
    {
        WeekSchedulerGrid.Children.Clear();
        WeekSchedulerGrid.RowDefinitions.Clear();
        WeekSchedulerGrid.ColumnDefinitions.Clear();
        HeaderDaysGrid.Children.Clear();

        CreateColumnDefinition(WeekSchedulerGrid, NUMBER_OF_COLUMNS, GRID_COLUMN_WIDTH);
        CreateRowDefinition(ref WeekSchedulerGrid, _numberOfRows);

        for(int row = 0; row < _numberOfRows; row++)
        {
            TextBlock textBlock = new TextBlock { Text = "Some row name"};

            Grid.SetRow(textBlock, row);
            Grid.SetColumn(textBlock, 0);
            WeekSchedulerGrid.Children.Add(textBlock);
        }

        foreach (var item in _headerList)
        {
            HeaderDaysGrid.Children.Add(item);
        }
    }

    private void CreateRowDefinition(ref SchedulerGrid grid, int numberOfRows)
    {
        for (int row = 0; row < numberOfRows; row++)
        {
            RowDefinition gridRow = new RowDefinition();
            gridRow.MinHeight = 100;
            gridRow.MaxHeight = 300;

            grid.RowDefinitions.Add(gridRow);
        }
    }

    private void CreateColumnDefinition(ref SchedulerGrid grid, int numberOfColumns, int columnWidth)
    {
        for (int colIndex = 0; colIndex < numberOfColumns; colIndex++)
        {
            ColumnDefinition gridCol = new ColumnDefinition();
            grid.ColumnDefinitions.Add(gridCol);

            if (colIndex == 0)
            {
                gridCol.Width = new GridLength(1, GridUnitType.Star);
                gridCol.MaxWidth = 150;
            }
            else
            {
                gridCol.Width = columnWidth;
            }
        }
    }

With this solution I am not sure how to implement 'fixed rows' and to have functionalities described on the picture. Code behind, XAML and binding or something else?

I probably can't join 'fixed rows' and all cells in one grid - because of scroll bar. 'Fixed rows', 'fixed header' and 'all cells' needs to be separate grid? But how to create such a layout and desired behavior? I'm really not sure.

In addition, this custom grid is used like scheduler and shows some custom events(user controls) in his cells, so I probably can't use DataGrid. And sorry for bad English.

Upvotes: 2

Views: 2826

Answers (2)

gzsun
gzsun

Reputation: 329

I resolved the problem on next way. I have three grids. If you look at the image 'Fixed header', 'Fixed rows' and 'Gray surface for content' are separate grids now. Also I have three scroll viewers. Only the scroll viewer for content is visible. Other scroll viewers are set as hidden, but I update theirs values in code behind when content scroll viewer position is changed.

XAML

<Grid">
    <Grid.RowDefinitions>
        <RowDefinition Height="40" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200"/>
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <ScrollViewer x:Name="HeaderDaysScrolViewer" CanContentScroll="True" Grid.Row="0" Grid.Column="1" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
        <Grid x:Name="HeaderDaysGrid" Margin="10,0,0,0" ShowGridLines="False"/>
    </ScrollViewer>

    <ScrollViewer x:Name="VerticalScrolViewer" CanContentScroll="True" Grid.Row="1" Grid.Column="0" 
                  HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
        <Grid x:Name="VerticalDataGrid" ShowCustomGridLines="True" GridLineThickness="0.5" GridLineBrush="#FF434343"/>
    </ScrollViewer>

    <ScrollViewer x:Name="SchedulerScrolViewer" HorizontalScrollBarVisibility="Visible" VerticalAlignment="Stretch"
                    VerticalScrollBarVisibility="Visible" ScrollChanged="SchedulerScrolViewer_ScrollChanged" Grid.Row="1" Grid.Column="1" Margin="10,0,0,5">
        <Grid>
            <Grid x:Name="WeekSchedulerGrid" ShowCustomGridLines="False" ScrollViewer.CanContentScroll="True"/>
        </Grid>
    </ScrollViewer>
</Grid>

Code behind

private void SchedulerScrolViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        HeaderDaysScrolViewer.ScrollToHorizontalOffset(e.HorizontalOffset);
        OverviewTypesScrolViewer.ScrollToVerticalOffset(e.VerticalOffset);
    }

Upvotes: 2

user3164339
user3164339

Reputation: 166

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition x:Name="HeaderDays" Height="35"/>
    <RowDefinition x:Name="ScrollAreaRows" Height="600"/>
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinition x:Name="HeaderRows" Width="35"/>
    <ColumnDefinition x:Name="ScrollAreaColumns" Width="600"/>
  </Grid.ColumnDefinitions>

  <ScrollViewer x:Name="DataScroller" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Visible" Grid.Row="1" Grid.Column="1">
    <Grid x:Name="DataGrid" Height="{Binding Height, ElementName=ScrollAreaRows, UpdateSourceTrigger=PropertyChanged}" Widht="{Binding Width, ElementName=ScrollAreaColumns, UpdateSourceTrigger=PropertyChanged}">
  </ScrollViewer>

the grid named "DataGrid" is the one that should scroll horizontally and vertically, while letting row0 and column0 of the outside grid to visually "stay static".

Upvotes: 0

Related Questions