Wpf: How to freeze last column in WPF datagrid ?

Created on 17 Feb 2020  路  7Comments  路  Source: dotnet/wpf

I need to freeze last column of the WPF datagrid. When I set the FrozenColumnCount as 1, then it will freeze only leftmost column but I need to freeze right most column. (that means I need to prevent column from moving). How to achieve this? Seems like need to write custom extension. But I don't have any idea of how to do that? Any guidance or advice will be much appreciated!

issue-type-question

All 7 comments

This is probably not possible. The doc says "Frozen columns are always the leftmost columns in display order". There are no hooks into DataGrid's layout that support any other policy.

You can easily change the display order of columns to put the special column at the left, where you can freeze it.

I don't know what your requirements are, but you might be able to fake it by using FlowDirection="RightToLeft" (and fixing the rest with CellStyle etc.). No need to say you break the built-in LTR/RTL support though.

EDIT: To clarify, it will still need to be the first column that is frozen, but it'll be frozen to the right side.

Thanks @miloush and @SamBent

I tried this alternative solution you have mentioned @miloush . Last column can be freezed.But then so many other issues are rising compared to normal WPF data-grid behavior. Such as when columns are resizing,It resizes to left side but normal grid columns are resizing to right side etc.Anyway thanks for the feedback.

@Siyumii have you thought about having two DataGrids next to each other, with the right one having just the column you want to be fixed?

@miloush I hope his approach will not be applicable for my scenario since No of columns displayed in the grid, is not static. In the application currently I'm developing, we can add and remove no of columns to be displayed. So this approach will not be better solution for this scenario.Anyway thanks again for the help !!

@Siyumii You can still do it dynamically, just not in XAML. Quick prototype:

<Window x:Class="FrozenGrid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="450" Width="800">
    <DockPanel Margin="50">
        <Slider DockPanel.Dock="Bottom" Value="5" Minimum="0" Maximum="6" 
                TickFrequency="1" IsSnapToTickEnabled="True" ValueChanged="OnSliderSlided" />

        <DataGrid DockPanel.Dock="Right" Name="_frozen" BorderThickness="0,1,1,1" AutoGenerateColumns="False" ScrollViewer.ScrollChanged="OnFrozenScrolled" HorizontalScrollBarVisibility="Visible">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding [5]}" Header="F" />
            </DataGrid.Columns>
        </DataGrid>
        <DataGrid Name="_grid" AutoGenerateColumns="False" VerticalScrollBarVisibility="Hidden" ScrollViewer.ScrollChanged="OnGridScrolled">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding [0]}" Header="A" />
                <DataGridTextColumn Binding="{Binding [1]}" Header="B" />
                <DataGridTextColumn Binding="{Binding [2]}" Header="C" />
                <DataGridTextColumn Binding="{Binding [3]}" Header="D" />
                <DataGridTextColumn Binding="{Binding [4]}" Header="E" />
            </DataGrid.Columns>
        </DataGrid>
    </DockPanel>
</Window>

```C#
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace FrozenGrid
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();

        double[][] data = new double[50][];
        Random random = new Random();
        for (int i = 0; i < 50; i++)
            data[i] = Enumerable.Range(0, 10).Select(r => random.NextDouble()).ToArray();

        _grid.ItemsSource = data;
        _frozen.ItemsSource = data;
    }

    private ScrollViewer _gridScroll;
    private ScrollViewer _frozenScroll;
    private void OnGridScrolled(object sender, ScrollChangedEventArgs e) => OnScrolled(_frozen, ref _frozenScroll, e);
    private void OnFrozenScrolled(object sender, ScrollChangedEventArgs e) => OnScrolled(_grid, ref _gridScroll, e);
    private void OnScrolled(DataGrid targetGrid, ref ScrollViewer targetScroll, ScrollChangedEventArgs e)
    {
        if (targetScroll == null)
            targetScroll = (ScrollViewer)VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(targetGrid, 0), 0);  // Grid > DG_ScrollViewer

        targetScroll.ScrollToVerticalOffset(e.VerticalOffset);
    }

    private void OnSliderSlided(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        if (!IsInitialized)
            return;

        if (e.NewValue > e.OldValue)
        {
            int freeColumns = (int)(e.NewValue - e.OldValue);
            for (int i = 0; i < freeColumns; i++)
            {
                DataGridColumn c = _frozen.Columns.First();
                _frozen.Columns.Remove(c);
                _grid.Columns.Add(c);
            }
        }
        else
        {
            int fixColumns = (int)(e.OldValue - e.NewValue);
            for (int i = 0; i < fixColumns; i++)
            {
                DataGridColumn c = _grid.Columns.Last();
                _grid.Columns.Remove(c);
                _frozen.Columns.Insert(0, c);
            }
        }

        _grid.Visibility = _grid.Columns.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
        _frozen.Visibility = _frozen.Columns.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
        _grid.VerticalScrollBarVisibility = _frozen.Columns.Count < 1 ? ScrollBarVisibility.Visible : ScrollBarVisibility.Hidden;
    }
}

}
```

Move the slider to adjust the number of frozen columns. You could turn this into a user control for simple reuse.

Was this page helpful?
0 / 5 - 0 ratings