Как проскролить DataGrid который в ScrollView?

140
16 марта 2022, 05:40

Мне кажется это достаточно общая ситуация, но я не могу найти правильное решение.

У меня есть DataGrid в ScrollViewer и в DataGrid установлены такие параметры

MinHeight="350"
MaxHeight="350"

То есть это значит, что DataGrid имеет постоянный размер и сидит но внутри ScrollViewer как часть страницы. Он может содержать (пример) 300 элементов.

Что мне нужно - когда юзер держит курсор на DataGrid и скролит вниз, то ScrollViewer не должен скролиться и как только юзер доскролил до конца DataGrid то вот сейчас ScrollViewer получает скрол ивент.

Как это сделать?

Answer 1
XAML
<Window x:Class="so_wpf_test_1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:so_wpf_test_1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Border Padding="10" Background="Yellow">
        <ScrollViewer x:Name="sv">
            <StackPanel Orientation="Vertical">
                <DataGrid x:Name="dg" MinHeight="350" MaxHeight="350" ScrollViewer.ScrollChanged="dg_ScrollChanged">
                </DataGrid>
                <Button>A button that doesn't do anything</Button>
                <DataGrid x:Name="dg2" MinHeight="350" MaxHeight="350"  ScrollViewer.ScrollChanged="dg_ScrollChanged">
                </DataGrid>
            </StackPanel>
        </ScrollViewer>
    </Border>
</Window>
Code-behind
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace so_wpf_test_1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var items = new ObservableCollection<Item>();
            for (int i = 1; i <= 50; ++i)
            {
                items.Add(new Item($"test {i}", $"abcd {i}", $"dcba {i}"));
            }
            dg.ItemsSource = items;
            dg2.ItemsSource = items;
            dg.PreviewMouseWheel += Dg_PreviewMouseWheel;
            dg2.PreviewMouseWheel += Dg_PreviewMouseWheel;
        }
        private void Dg_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            var m_dg = sender as DataGrid;
            var scroll = GetScrollViewer(m_dg);
            // if scrolling to bottom and beyond...
            if (e.Delta < 0 && scroll.VerticalOffset == scroll.ScrollableHeight)
            {
                sv.ScrollToVerticalOffset(sv.VerticalOffset - e.Delta);
            }
            // if scrolling to top and beyond...
            else if (e.Delta > 0 && scroll.VerticalOffset == 0)
            {
                sv.ScrollToVerticalOffset(sv.VerticalOffset - e.Delta);
            }
            else
            {
                // nothing special: scroll the dg if it is the case (WPF does this automatically)
            }
            int idx = 0;
            // if scrolling down
            if (e.Delta < 0)
            {
                // see the sketch
                idx = dictLastVisible[m_dg] + 1;
                Debug.WriteLine($"FROM {(m_dg == dg ? 1 : 2)} DOWN {idx}");
            }
            // if scrolling up
            else if (e.Delta > 0)
            {
                // see the sketch
                idx = dictFirstVisible[m_dg] - 1;
                Debug.WriteLine($"FROM {(m_dg == dg ? 1 : 2)} UP {idx}");
            }
            // if the index does not represent nothing
            if (idx != 0)
            {
                // transform index to be 0-based
                --idx;
                // scroll that row into view
                m_dg.ScrollIntoView(m_dg.Items[idx]);
            }
        }
        /// <summary>
        /// Source: https://stackoverflow.com/a/41136328/258462
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public static ScrollViewer GetScrollViewer(UIElement element)
        {
            if (element == null) return null;
            ScrollViewer retour = null;
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element) && retour == null; i++)
            {
                if (VisualTreeHelper.GetChild(element, i) is ScrollViewer)
                {
                    retour = (ScrollViewer)(VisualTreeHelper.GetChild(element, i));
                }
                else
                {
                    retour = GetScrollViewer(VisualTreeHelper.GetChild(element, i) as UIElement);
                }
            }
            return retour;
        }
        Dictionary<DataGrid, int> dictLastVisible = new Dictionary<DataGrid, int>();
        Dictionary<DataGrid, int> dictFirstVisible = new Dictionary<DataGrid, int>();
        private void dg_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            var dg = (DataGrid)sender;
            int idxb = -1, idxe = -1; // b = beginning, e = end; both invalid initially
            // from the first row towards the last row
            for (int i = 0; i < dg.Items.Count; i++)
            {
                var v = (DataGridRow)dg.ItemContainerGenerator.ContainerFromItem(dg.Items[i]);
                if (v != null)
                {
                    idxb = i + 1; // compute the beginning row in the viewport
                    break;
                }
            }
            // from the beginning row towards the last row
            for (int i = idxb + 1; i < dg.Items.Count; i++)
            {
                var v = (DataGridRow)dg.ItemContainerGenerator.ContainerFromItem(dg.Items[i]);
                if (v == null)
                {
                    idxe = i - 1 + 1; // compute the end row in the viewport
                    break;
                }
            }
            // store the two indices in two dictionaries
            dictFirstVisible[dg] = idxb;
            dictLastVisible[dg] = idxe;
        }
    }
    public class Item
    {
        public string A { get; set; }
        public string B { get; set; }
        public string C { get; set; }
        public Item(string a, string b, string c)
        {
            A = a;
            B = b;
            C = c;
        }
    }
}
Possibly helping sketch

READ ALSO
Не получается запустить с помощью события, строку в методе void

Не получается запустить с помощью события, строку в методе void

возникла ситуация, именно через void не получается запустить программу, а вернее не понимаю, как это сделать

89
Не проигрываются анимации на Unity при создании 2D проекта

Не проигрываются анимации на Unity при создании 2D проекта

Столкнулся с проблемой во время прописывания анимаций для персонажаПри нажатии на клавиши движения срабатывает лишь 1 из всех имеющихся...

70
Как называется фильтр, который не позволяет отправлять часть объекта?

Как называется фильтр, который не позволяет отправлять часть объекта?

Я возвращаю объект из метода в View, мне нужно, чтобы часть полей View не видела, как это делается, кажется видел когда-то давно что можноНо не могу...

69
Unescape последовательности вида &quot;\U0001f973&quot;

Unescape последовательности вида "\U0001f973"

У меня есть строка str = "\U0001f973"Это escape sequence для эмодзи

86