【问题标题】:WPF - DataGrid nested in Expander no scrolling datagrid contentWPF - 嵌套在 Expander 中的 DataGrid 没有滚动数据网格内容
【发布时间】:2017-09-10 09:48:58
【问题描述】:

我在 WPF 应用程序上有 Expander 和嵌套的 DataGrid - 它创建了用户控件。我为数据(来自数据库)列表中的每个元素在代码隐藏上创建此控件。最后,我有一个列表,其中每个元素都使用嵌套的 DataGrid 扩展。当我开发项目时,我会看到 DataDrid,但是当我要开发许多组件时,我必须滚动内容。当光标在扩展器上时,元素滚动起作用,但是当我将鼠标悬停在 DataGrid 上时,滚动不起作用。

示例代码:

<ScrollViewer HorizontalAlignment="Left">
<DockPanel>
      <Expander x:Name="Expander1" Expanded="Expander1_Expanded">
        <Expander.Content>
            <DataGrid x:Name="DataGrid1" MouseLeftButtonUp="DataGrid1_MouseLeftButtonDown"  ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden" >        
                <DataGrid.CellStyle>
                    <Style TargetType="DataGridCell">
                        <Setter Property="BorderThickness" Value="0"/>
                    </Style>
                </DataGrid.CellStyle>
                <DataGrid.Columns>
                    <DataGridTextColumn Header="name1" Binding="{Binding Name}" IsReadOnly="True" />
                    <DataGridTextColumn Header="name2" Binding="{Binding Value}" IsReadOnly="True"/>
                    <DataGridTextColumn Header="name3" Binding="{Binding UnitName}" IsReadOnly="True"/>
                </DataGrid.Columns>
            </DataGrid>
        </Expander.Content>
        <Expander.Style>
            <Style TargetType="Expander">
                <Setter Property="IsExpanded" Value="False" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsExpanded, RelativeSource={RelativeSource Self}}" Value="True">
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Expander.Style>
    </Expander>

// and mor expander, added from codebehind 

</DockPanel>
</ScrollViewer>

最后网格:

当鼠标在绿色环向右滚动时起作用

【问题讨论】:

    标签: c# wpf datagrid expander


    【解决方案1】:

    发生这种情况是因为DataGrid 本身可能包含ScrollViewer,当您拥有的物品多于给定高度内的物品时,就会出现该ScrollViewer。在这些情况下,您将希望允许 DataGrid 首先尝试处理滚动事件,如果它不知道该怎么做,例如当您尝试向下滚动时已经在底部,将滚动事件传递给其父级。

    现在,您的DataGrids 看起来实际上不可滚动,这意味着以下可能有点矫枉过正,但是通过对鼠标滚轮处理程序进行以下修改来获得实现上述目的的通用解决方案:

    /// <summary>
    /// Helper for allowing scroll events to pass from a <see cref="DataGrid"/> to its parent.
    /// This ensures that a "scroll down" event occurring at an already scrolled-down
    /// <see cref="DataGrid"/> will be passed on to its parent, which might be able to handle
    /// it instead.
    /// </summary>
    public class DataGridScrollCorrector
    {
        public static bool GetFixScrolling(DependencyObject obj) =>
            (bool)obj.GetValue(FixScrollingProperty);
    
        public static void SetFixScrolling(DependencyObject obj, bool value) =>
            obj.SetValue(FixScrollingProperty, value);
    
        public static readonly DependencyProperty FixScrollingProperty =
            DependencyProperty.RegisterAttached("FixScrolling", typeof(bool), typeof(DataGridScrollCorrector), new FrameworkPropertyMetadata(false, OnFixScrollingPropertyChanged));
    
        private static void OnFixScrollingPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            var grid = sender as DataGrid;
            if (grid == null)
                throw new ArgumentException("The dependency property can only be attached to a DataGrid", nameof(sender));
            if ((bool)e.NewValue)
                grid.PreviewMouseWheel += HandlePreviewMouseWheel;
            else
                grid.PreviewMouseWheel -= HandlePreviewMouseWheel;
        }
    
        /// <summary>
        /// Finds the first child of a given type in a given <see cref="DependencyObject"/>.
        /// </summary>
        /// <typeparam name="T">The type of the child to search for.</typeparam>
        /// <param name="depObj">The object whose children we are interested in.</param>
        /// <returns>The child object.</returns>
        private static T FindVisualChild<T>(DependencyObject depObj) where T : DependencyObject
        {
            if (depObj == null) return null;
            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                var visualChild = child as T;
                if (visualChild != null) return visualChild;
    
                var childItem = FindVisualChild<T>(child);
                if (childItem != null) return childItem;
            }
            return null;
        }
    
        /// <summary>
        /// Attempts to scroll the <see cref="ScrollViewer"/> in the <see cref="DataGrid"/>.
        /// If no scrolling occurs, pass the event to a parent.
        /// </summary>
        private static void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            var grid = sender as DataGrid;
            var viewer = FindVisualChild<ScrollViewer>(grid);
    
            if (viewer != null)
            {
                // We listen on changes to the ScrollViewer's scroll offset; if that changes
                // we can consider our event handled. In case the ScrollChanged event is never
                // raised, we take this to mean that we are at the top/bottom of our scroll viewer,
                // in which case we provide the event to our parent.
                ScrollChangedEventHandler handler = (senderScroll, eScroll) =>
                    e.Handled = true;
    
                viewer.ScrollChanged += handler;
                // Scroll +/- 3 rows depending on whether we are scrolling up or down. The
                // forced layout update is necessary to ensure that the event is called
                // immediately (as opposed to after some small delay).
                double oldOffset = viewer.VerticalOffset;
                double offsetDelta = e.Delta > 0 ? -3 : 3;
                viewer.ScrollToVerticalOffset(oldOffset + offsetDelta);
                viewer.UpdateLayout();
                viewer.ScrollChanged -= handler;
            }
    
            if (e.Handled) return;
            e.Handled = true;
            var eventArg =
                new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
                {
                    RoutedEvent = UIElement.MouseWheelEvent,
                    Source = sender
                };
            var parent = ((Control)sender).Parent as UIElement;
            parent?.RaiseEvent(eventArg);
        }
    }
    

    这里,硬编码的3DataGrid 中要滚动的行数。然后,您可以将此校正器应用于所有相关的DataGrids。例如,要在应用程序中的所有网格上使用它,您可以将其添加到 Application.Resources 中的 App.xaml 中,如下所示:

    <Application x:Class="WpfApplication1.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:WpfApplication1"
                 StartupUri="MainWindow.xaml">
        <Application.Resources>
            <Style TargetType="DataGrid">
                <Setter Property="local:DataGridScrollCorrector.FixScrolling" Value="True" />
            </Style>
        </Application.Resources>
    </Application>
    

    Credit:这个解决方案在某种程度上基于in this blog post,但它的功能仅限于DataGrid(根据我的经验,否则它会破坏一堆其他不准备的控件隧道他们的事件)。

    【讨论】:

    • 太棒了。您提供的代码和博客链接解决了我的问题
    猜你喜欢
    • 2011-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-14
    • 2013-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多