Share via

DataGrid across multiple pages of a Fixed Document

BigH61 581 Reputation points
Oct 21, 2023, 4:22 PM

Would it be possible to split a DataGrid across multiple pages of a FixedDocument while retaining the Header row on each page as well as when adjusting column width on one page it is reflected in subsequent pages.

I have thought of created a new DataGrid per page based on page Size and row Height but that results in each grid being independent of the other and no way to reflect the column width across each page?

Within the demonstration project there is also the option to hide the first Column but I have noticed that as the number of rows increases within the DataGrid there is some displacement to the right of the HeaderRow when a column Visibility is changed. When the mouse enters the FixedDocument this is often corrected. How can I stop this error?

The bases on my project is shown within the link below

FixedDocumentProject

Thank you in advance for your assistance

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,833 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,328 questions
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. gekka 11,296 Reputation points MVP
    Oct 22, 2023, 12:20 PM
    <Window x:Class="Gekka.WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:Gekka.WpfApplication1"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            Title="MainWindow" Height="800" Width="800">
    
        <Window.Resources>
            <DataTemplate x:Key="pageItemTemplate">
    
                <Grid Grid.Column="1" Margin="20" HorizontalAlignment="Stretch">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition />
                    </Grid.RowDefinitions>
    
                    <Grid Grid.Row="0" Height="{Binding Path=ColumnHeader.ActualHeight}" 
                          MouseLeftButtonDown="HeaderGrid_MouseLeftButtonDown"
                          MouseRightButtonUp="HeaderGrid_MouseRightButtonUp"
                          MouseMove="HeaderGrid_MouseMove">
                        <Grid.Background>
                            <VisualBrush Visual="{Binding}" Stretch="None"
                                         AlignmentX="Left" AlignmentY="Top"                                                         
                                         TileMode="None">
                            </VisualBrush>
                        </Grid.Background>
                    </Grid>
    
                    <Grid x:Name="grid" Grid.Row="1" Grid.RowSpan="2" ClipToBounds="True" Width="360" Height="300"
                          Tag="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Tag}">
                        <Grid.Background>
                            <VisualBrush Visual="{Binding}" Stretch="None" 
                                            AlignmentX="Left" AlignmentY="Top"
                                            TileMode="None"
                                            ViewboxUnits="Absolute"
                                            ViewportUnits="RelativeToBoundingBox">
                                <VisualBrush.Viewbox>
                                    <MultiBinding>
                                        <MultiBinding.Converter>
                                            <local:PageViewBoxConverter />
                                        </MultiBinding.Converter>
                                        <Binding ElementName="grid" Path="DataContext.ColumnHeader.ActualHeight" />
                                        <Binding ElementName="grid" Path="ActualWidth" />
                                        <Binding ElementName="grid" Path="ActualHeight" />
                                        <Binding ElementName="grid" Path="Tag"/>
                                    </MultiBinding>
                                </VisualBrush.Viewbox>
                            </VisualBrush>
                        </Grid.Background>
                    </Grid>
    
                    <Border Grid.Row="1" BorderBrush="{Binding BorderBrush}" BorderThickness="0,0,0,1" VerticalAlignment="Bottom"
                            SnapsToDevicePixels="True"/>
                </Grid>
    
            </DataTemplate>
    
            <ContextMenu x:Key="ColumnContextMenu">
                <MenuItem Header="Hide" Click="HideColumn_Click" />
                <MenuItem Header="ShowAll" Click="ShowAllColumn_Click" />
            </ContextMenu>
        </Window.Resources>
    
        <DockPanel>
            <Grid >
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
    
                <DockPanel>
                    <CheckBox x:Name="chkHide" IsChecked="True" Content="First Column Hide" DockPanel.Dock="Top"
                              Checked="chkHide_Checked" Unchecked="chkHide_Checked" FontSize="20" Margin="5"/>
    
                    <ScrollViewer Margin="5">
                        <!-- This DataGrid is target for VisualBrush -->
                        <local:DataGridEx x:Name="dg" AutoGenerateColumns="False" 
                                  ScrollViewer.VerticalScrollBarVisibility="Hidden"
                                  ScrollViewer.HorizontalScrollBarVisibility="Auto"
                                  RowHeaderWidth="30" Width="400" HorizontalAlignment="Left"
                                  TextElement.FontSize="20"
                                  ItemsSource="{Binding}" DataContext="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789">
                            <DataGrid.Columns>
                                <DataGridCheckBoxColumn Header="Column1" IsReadOnly="True"/>
                                <DataGridTextColumn Header="Column2" Binding="{Binding Mode=OneWay}" IsReadOnly="True"/>
                            </DataGrid.Columns>
                            <DataGrid.ColumnHeaderStyle>
                                <Style TargetType="{x:Type DataGridColumnHeader}">
                                    <Setter Property="ContextMenu" Value="{StaticResource ColumnContextMenu}"/>
                                </Style>
                            </DataGrid.ColumnHeaderStyle>
                        </local:DataGridEx>
                    </ScrollViewer>
                </DockPanel>
    
                <Grid Grid.Column="1" >
                    <DocumentViewer>
                        <FixedDocument>
                            <PageContent>
                                <FixedPage Width="400" Height="400" >
                                    <ContentPresenter Content="{Binding ElementName=dg}" ContentTemplate="{StaticResource pageItemTemplate}">
                                        <ContentPresenter.Tag>
                                            <sys:Int32>0</sys:Int32>
                                        </ContentPresenter.Tag>
                                    </ContentPresenter>
                                </FixedPage>
    
                            </PageContent>
    
                            <PageContent>
                                <FixedPage Width="400" Height="400" >
                                    <ContentPresenter Content="{Binding ElementName=dg}" ContentTemplate="{StaticResource pageItemTemplate}">
                                        <ContentPresenter.Tag>
                                            <sys:Int32>1</sys:Int32>
                                        </ContentPresenter.Tag>
                                    </ContentPresenter>
    
                                </FixedPage>
                            </PageContent>
    
                            <PageContent>
                                <FixedPage Width="400" Height="400" >
                                    <ContentPresenter Content="{Binding ElementName=dg}" ContentTemplate="{StaticResource pageItemTemplate}">
                                        <ContentPresenter.Tag>
                                            <sys:Int32>2</sys:Int32>
                                        </ContentPresenter.Tag>
                                    </ContentPresenter>
    
                                </FixedPage>
                            </PageContent>
                        </FixedDocument>
                    </DocumentViewer>
                </Grid>
            </Grid>
        </DockPanel>
    </Window>
    
    namespace Gekka.WpfApplication1
    {
        using System;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Controls.Primitives;
        using System.Windows.Input;
        using System.Windows.Media;
        using System.Globalization;
    
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void chkHide_Checked(object sender, RoutedEventArgs e)
            {
                if (dg != null)
                {
                    var chk = (CheckBox)sender;
                    if (chk.IsChecked == true)
                    {
                        this.dg.Columns[0].Visibility = Visibility.Visible;
                    }
                    else
                    {
                        this.dg.Columns[0].Visibility = Visibility.Collapsed;
                    }
                }
            }
    
            private void HeaderGrid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                this.dg.TransformMouseLeftButtonDown(sender, e);
            }
    
            private void HeaderGrid_MouseMove(object sender, MouseEventArgs e)
            {
                this.dg.TransformMouseMove(sender, e);
            }
    
            private void HeaderGrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
            {
                this.dg.TransformMouseRightButtonUp(sender, e);
            }
    
            private void HideColumn_Click(object sender, RoutedEventArgs e)
            {
                var mi=(MenuItem)sender;
                if (mi.Parent is ContextMenu cm)
                {
                    if (cm.PlacementTarget is  DataGridColumnHeader header)
                    {
                        header.Column.Visibility = Visibility.Collapsed;
                    }
                }
            }
    
            private void ShowAllColumn_Click(object sender, RoutedEventArgs e)
            {
                foreach (var column in this.dg.Columns)
                {
                    column.Visibility = Visibility.Visible;
                }
            }
        }
    
        class DataGridEx : DataGrid
        {
            public DataGridEx()
            {
                this.Loaded += DagaGridEx_Loaded;
            }
    
            private void DagaGridEx_Loaded(object sender, RoutedEventArgs e)
            {
                var sv = this.Template.FindName("DG_ScrollViewer", this) as ScrollViewer;
                this.ColumnHeader = sv?.Template.FindName("PART_ColumnHeadersPresenter", sv) as FrameworkElement;
            }
    
            public FrameworkElement ColumnHeader
            {
                get { return (FrameworkElement)GetValue(HeaderProperty); }
                private set { SetValue(HeaderProperty, value); }
            }
    
            public static readonly DependencyProperty HeaderProperty
                = DependencyProperty.Register(nameof(ColumnHeader), typeof(FrameworkElement), typeof(DataGridEx), new PropertyMetadata(null));
    
            private bool TryGetControl<T>(Point pos, out T? thumb) where T : Control
            {
                thumb = null;
                DependencyObject? d = this.InputHitTest(pos) as DependencyObject;
                while (d != null && d != this)
                {
                    thumb = d as T;
                    if (thumb != null)
                    {
                        return true;
                    }
                    if (d is Visual)
                    {
                        d = VisualTreeHelper.GetParent(d);
                    }
                    else
                    {
                        d = LogicalTreeHelper.GetParent(d);
                    }
                }
                return false;
            }
    
            private bool TryGetThumb(Point pos, out System.Windows.Controls.Primitives.Thumb? thumb)
            {
                return TryGetControl<System.Windows.Controls.Primitives.Thumb>(pos, out thumb);
            }
    
            public void TransformMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                if (sender is FrameworkElement fe)
                {
                    var pos = e.GetPosition(fe);
    
                    if (TryGetThumb(pos, out var thumb) && thumb.IsEnabled)
                    {
                        thumb.RaiseEvent(e);
                        e.Handled = true;
                    }
                    else if (this.InputHitTest(pos) is UIElement ui)
                    {
                        ui.RaiseEvent(e);
                        e.Handled = true;
                    }
                }
            }
    
            public void TransformMouseRightButtonUp(object sender, MouseButtonEventArgs e)
            {
                if (sender is FrameworkElement fe)
                {
                    var pos = e.GetPosition(fe);
    
                    DependencyObject? d = this.InputHitTest(pos) as DependencyObject;
                    while (d != null)
                    {
                        if (d is FrameworkElement fe2 && fe2.ContextMenu != null)
                        {
                            fe2.ContextMenu.PlacementTarget = fe2;
                            fe2.ContextMenu.IsOpen = true;
                            e.Handled = true;
                            return;
                        }
                        if (d is Visual)
                        {
                            d = VisualTreeHelper.GetParent(d);
                        }
                        else
                        {
                            d = LogicalTreeHelper.GetParent(d);
                        }
                    }
                }
            }
    
            public void TransformMouseMove(object sender, MouseEventArgs e)
            {
                if (sender is FrameworkElement fe)
                {
                    var pos = e.GetPosition(fe);
    
                    if (TryGetThumb(pos, out var thumb) && thumb.IsEnabled)
                    {
                        fe.Cursor = Cursors.SizeWE;
                    }
                    else
                    {
                        fe.Cursor = Cursors.Arrow;
                    }
                }
            }
        }
    
        class PageViewBoxConverter : System.Windows.Data.IMultiValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
                if (values != null && values.Length >= 4
                && values[0] is double headerHeight
                && values[1] is double boxWidth
                && values[2] is double boxHeight
                && values[3] is int page)
                {
                    return new System.Windows.Rect(0, headerHeight + page * boxHeight, boxWidth, boxHeight);
                }
                return new Rect(0, 0, 0, 0);
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    
    }
    

    edit: Add Mouse Event on ColumnHeader


  2. Hui Liu-MSFT 48,631 Reputation points Microsoft External Staff
    Oct 23, 2023, 7:33 AM

    Hi,@BigH61. Welcome to Microsoft Q&A. Here is an example using PagingCollectionView on which you could perform simple operations like MoveToNextPage and MoveToPreviousPage.

    I've made the following update to your code. And using your code to show or hide the first row will also work successfully.

    MainWindow.xaml:

      <Window.DataContext>
            <vm:MainWindowViewModel/>
        </Window.DataContext>
    
        <Grid >
           
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="4*"/>
            </Grid.ColumnDefinitions>
            <StackPanel Grid.Column="0">
                <StackPanel  Orientation="Vertical">
    
                    <CheckBox Content="First Column Hide" Margin="10,20,10,10" Command="{Binding FirstColumnHideCommand}"/>
    
                </StackPanel>
                <StackPanel Orientation="Horizontal">
                    <Label Grid.Row="0" Margin="2">
                        <Label.Content>
                            <Binding Path="CurrentPage">
                                <Binding.StringFormat>Current Page: {0}</Binding.StringFormat>
                            </Binding>
                        </Label.Content>
                    </Label>
                    <Button Content="Next" Command="{Binding NextCommand}" Margin="2"/>
                    <Button Content="Previous" Command="{Binding PreviousCommand}" Margin="2"/>
                </StackPanel>
            </StackPanel>
    
            <DocumentViewer Grid.Column="1" Document="{Binding FixedDocument}" >
               
            </DocumentViewer>
        </Grid>
    
    
    

    MainWindowViewModel:

    
       public RelayCommand FirstColumnHideCommand { get; set; }
            public RelayCommand PreviousCommand { get; set; }
            public RelayCommand NextCommand { get; set; }
            private readonly PagingCollectionView _cview;
          
            public MainWindowViewModel()
            {
                FirstColumnHideCommand = new RelayCommand(FirstColumnHide);
                PreviousCommand = new RelayCommand(OnPreviousClicked);
                NextCommand = new RelayCommand(OnNextClicked);
                ViewItemsCollection = new ObservableCollection<StampModel>();
                ViewItemsCollection.Add(new StampModel { ImageSource = new BitmapImage(new Uri($"pack://application:,,,/FixedDocumentTest;component/Resources/Face.jpg")), Id = 1, Country = "Aden" });
                ViewItemsCollection.Add(new StampModel { Id = 2, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 3, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 4, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 5, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 6, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 7, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 8, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 9, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 10, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 11, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 12, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 13, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 14, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 15, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 16, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 17, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 18, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 19, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 20, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 21, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 22, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 23, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 24, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 25, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 26, Country = "Australia" });
                ViewItemsCollection.Add(new StampModel { Id = 27, Country = "USA" });
                ViewItemsCollection.Add(new StampModel { Id = 28, Country = "Australia" });
                //Error Displacement of the Header Row starts hear when Row Height 40
                ViewItemsCollection.Add(new StampModel { Id = 29, Country = "USA" });
               
               
                _cview= new PagingCollectionView(ViewItemsCollection, 10);
                DrawPrintPreview();
            }
    
            private void OnPreviousClicked(object obj)
            {
                this._cview.MoveToPreviousPage();
            }
    
    
    
            private void OnNextClicked(object obj)
            {
                this._cview.MoveToNextPage();
            }
    
    
            public void CreateDataGrid()
            {
                dataGrid = new DataGrid();
                dataGrid.IsReadOnly = true;
                dataGrid.AutoGenerateColumns = false;
                dataGrid.RowHeight = 40;
                dataGrid.HeadersVisibility = DataGridHeadersVisibility.Column;
                dataGrid.AlternatingRowBackground = new SolidColorBrush(Colors.AliceBlue);
                DataGrid();
                //   dataGrid.ItemsSource = ViewItemsCollection;
                dataGrid.ItemsSource = _cview;
            }
    

    PagingCollectionView:

     public class PagingCollectionView : CollectionView
        {
            private readonly IList _innerList;
            private readonly int _itemsPerPage;
    
            private int _currentPage = 1;
    
            public PagingCollectionView(IList innerList, int itemsPerPage)
                : base(innerList)
            {
                this._innerList = innerList;
                this._itemsPerPage = itemsPerPage;
            }
    
            public override int Count
            {
                get
                {
                    if (this._innerList.Count == 0) return 0;
                    if (this._currentPage < this.PageCount) // page 1..n-1
                    {
                        return this._itemsPerPage;
                    }
                    else // page n
                    {
                        var itemsLeft = this._innerList.Count % this._itemsPerPage;
                        if (0 == itemsLeft)
                        {
                            return this._itemsPerPage; // exactly itemsPerPage left
                        }
                        else
                        {
                            // return the remaining items
                            return itemsLeft;
                        }
                    }
                }
            }
    
            public int CurrentPage
            {
                get { return this._currentPage; }
                set
                {
                    this._currentPage = value;
                    this.OnPropertyChanged(new PropertyChangedEventArgs("CurrentPage"));
                }
            }
    
            public int ItemsPerPage { get { return this._itemsPerPage; } }
    
            public int PageCount
            {
                get
                {
                    return (this._innerList.Count + this._itemsPerPage - 1)
                        / this._itemsPerPage;
                }
            }
    
            private int EndIndex
            {
                get
                {
                    var end = this._currentPage * this._itemsPerPage - 1;
                    return (end > this._innerList.Count) ? this._innerList.Count : end;
                }
            }
    
            private int StartIndex
            {
                get { return (this._currentPage - 1) * this._itemsPerPage; }
            }
    
            public override object GetItemAt(int index)
            {
                var offset = index % (this._itemsPerPage);
                return this._innerList[this.StartIndex + offset];
            }
    
            public void MoveToNextPage()
            {
                if (this._currentPage < this.PageCount)
                {
                    this.CurrentPage += 1;
                }
                this.Refresh();
            }
    
            public void MoveToPreviousPage()
            {
                if (this._currentPage > 1)
                {
                    this.CurrentPage -= 1;
                }
                this.Refresh();
            }
        }
    
    

    The result:

    User's image

    User's image


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.