C# WPF-嵌套在Expander中的DataGrid无滚动DataGrid内容
我在WPF应用程序上安装了带有嵌套DataGrid的Expander,它创建用户控件。我在codebehind上为数据(来自数据库)列表中的每个元素创建此控件。最后,我有一个列表,其中每个元素都用嵌套的DataGrid进行了扩展。当我开发项目时,我会看到DataDrid,但是当我开发许多组件时,我必须滚动内容。当光标位于扩展器上时,元素滚动工作,但当我将鼠标悬停在DataGrid上时,滚动不工作 示例代码:C# WPF-嵌套在Expander中的DataGrid无滚动DataGrid内容,c#,wpf,datagrid,expander,C#,Wpf,Datagrid,Expander,我在WPF应用程序上安装了带有嵌套DataGrid的Expander,它创建用户控件。我在codebehind上为数据(来自数据库)列表中的每个元素创建此控件。最后,我有一个列表,其中每个元素都用嵌套的DataGrid进行了扩展。当我开发项目时,我会看到DataDrid,但是当我开发许多组件时,我必须滚动内容。当光标位于扩展器上时,元素滚动工作,但当我将鼠标悬停在DataGrid上时,滚动不工作 示例代码: <ScrollViewer HorizontalAlignment="Left"&
<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>
//和mor扩展器,从codebehind添加
最后,网格:
当鼠标位于绿色环向右滚动的位置时,会发生这种情况,因为
DataGrid
本身可能包含一个ScrollViewer
,当您拥有的项目超过给定高度时,将出现该视图。在这些情况下,您需要允许DataGrid
首先尝试处理滚动事件,如果它不知道如何处理,例如,当您尝试向下滚动时,同时已经在底部,请将滚动事件传递给其父级
现在,看起来您的DataGrid
s实际上是不可滚动的,这意味着以下内容可能有点过火,但通过对鼠标滚轮处理程序进行以下修改,可以获得实现上述功能的通用解决方案:
/// <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);
}
}
信用:该解决方案在某种程度上基于/受上述解决方案的启发,但它将其功能限制为
DataGrid
(因为根据我的经验,它将破坏一系列其他不准备隧道其事件的控件).之所以会发生这种情况,是因为数据网格本身可能包含一个ScrollViewer
,当您拥有的项目超过给定高度时,就会显示该滚动查看器。在这些情况下,您需要允许DataGrid
首先尝试处理滚动事件,如果它不知道如何处理,例如,当您尝试向下滚动时,同时已经在底部,请将滚动事件传递给其父级
现在,看起来您的DataGrid
s实际上是不可滚动的,这意味着以下内容可能有点过火,但通过对鼠标滚轮处理程序进行以下修改,可以获得实现上述功能的通用解决方案:
/// <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);
}
}
信用:这个解决方案在某种程度上是基于上面提到的一个,但它将其功能限制在DataGrid
(因为根据我的经验,它将打破一堆其他控件,而这些控件不准备将它们的事件隧道化)。这太棒了。你提供的代码和博客链接解决了我的问题,太棒了。你提供的代码和博客链接解决了我的问题