我有这个自定义数据网格。我想要实现的是,当用户将鼠标放在 DataGrid 内部(没有最大高度)时,它应该允许滚动 ScrollViewer 内部的整个父组件。但现在我必须将鼠标移到 DataGrid 之外才能滚动。
下面是我的代码:
<Style TargetType="{x:Type DataGrid}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="RowDetailsVisibilityMode" Value="Collapsed" />
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="HeadersVisibility" Value="None" />
<Setter Property="VerticalContentAlignment" Value="center" />
<Setter Property="GridLinesVisibility" Value="None" />
<Setter Property="EnableRowVirtualization" Value="False" />
<Setter Property="EnableColumnVirtualization" Value="True" />
<Setter Property="VirtualizingPanel.ScrollUnit" Value="Pixel" />
<Setter Property="VirtualizingPanel.VirtualizationMode" Value="Standard" />
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True" />
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
<Setter Property="BorderBrush" Value="Transparent" />
</Style>
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
// SOME OTHER COMPONENTS
<DataGrid ItemsSource="{Binding ListOfUsers}"
PreviewMouseWheel="ListView_PreviewMouseWheel">
// custom rows
</DataGrid>
背后代码:
private void ListView_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
// Find the ScrollViewer that contains this ListView
ScrollViewer scrollViewer = FindParentScrollViewer(PatientsListView);
if (scrollViewer != null)
{
// Scroll the ScrollViewer based on the mouse wheel delta
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);
e.Handled = true; // Mark the event as handled
}
}
private ScrollViewer FindParentScrollViewer(DependencyObject child)
{
if (child is null) return null;
for (DependencyObject parent = VisualTreeHelper.GetParent(child); parent != null; parent = VisualTreeHelper.GetParent(parent))
{
if (parent is ScrollViewer scrollViewer)
return scrollViewer;
}
return null;
}
我尝试将 ScrollViewer.CanContentScroll 值更改为 False 或添加 PreviewMouseWheel 事件,但似乎不起作用。
我偶然发现了这个网页,并将其实施到我的项目中,效果很好: https://serialseb.com/blog/2007/09/03/wpf-tips-6-preventing-scrollviewer-from/
所以,这就是我所做的: 首先,创建一个名为 ScrollViewerCorrector 的类来定义附加属性:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
public class ScrollViewerCorrector
{
public static bool GetFixScrolling(DependencyObject obj)
{
return (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(ScrollViewerCorrector),
new FrameworkPropertyMetadata(false, OnFixScrollingPropertyChanged));
private static List<MouseWheelEventArgs> _reentrantList = new List<MouseWheelEventArgs>();
private static void OnFixScrollingPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (sender is ScrollViewer viewer)
{
if ((bool)e.NewValue)
{
viewer.PreviewMouseWheel += HandlePreviewMouseWheel;
}
else
{
viewer.PreviewMouseWheel -= HandlePreviewMouseWheel;
}
}
}
private static void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var scrollControl = sender as ScrollViewer;
if (!e.Handled && scrollControl != null && !_reentrantList.Contains(e))
{
var previewEventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
{
RoutedEvent = UIElement.PreviewMouseWheelEvent,
Source = sender
};
var originalSource = e.OriginalSource as UIElement;
_reentrantList.Add(previewEventArg);
originalSource.RaiseEvent(previewEventArg);
_reentrantList.Remove(previewEventArg);
if (!previewEventArg.Handled && ((e.Delta > 0 && scrollControl.VerticalOffset == 0) ||
(e.Delta <= 0 && scrollControl.VerticalOffset >= scrollControl.ExtentHeight - scrollControl.ViewportHeight)))
{
e.Handled = true;
var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
{
RoutedEvent = UIElement.MouseWheelEvent,
Source = sender
};
var parent = (UIElement)((FrameworkElement)sender).Parent;
parent.RaiseEvent(eventArg);
}
}
}
}
接下来,在 XAML 中修改 ScrollViewer 以利用此附加属性。以下是如何将附加属性应用于外部和内部 ScrollViewer 的示例:
<Window x:Class="YourNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:YourNamespace"
Title="MainWindow" Height="423" Width="596">
<Window.Resources>
<Style TargetType="{x:Type ScrollViewer}">
<Setter Property="local:ScrollViewerCorrector.FixScrolling" Value="True" />
</Style>
</Window.Resources>
<Grid>
<ScrollViewer>
<StackPanel>
<TextBlock>Content before ListView</TextBlock>
<ScrollViewer Name="InnerScrollViewer" Height="235">
<StackPanel>
<TextBlock>Scrollable Content 1</TextBlock>
<TextBlock>Scrollable Content 2</TextBlock>
<TextBlock>Scrollable Content 3</TextBlock>
<ListView Background="Red" ItemsSource="{Binding PatientsNotVerified}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Right">
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}, {1}">
<Binding Path="Email" />
<Binding Path="Cell" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</ScrollViewer>
<TextBlock>Content after ListView</TextBlock>
</StackPanel>
</ScrollViewer>
</Grid>
</Window>