鼠标滚动在带有 wpf 数据网格和其他 UI 元素的滚动查看器中不起作用

Posted

技术标签:

【中文标题】鼠标滚动在带有 wpf 数据网格和其他 UI 元素的滚动查看器中不起作用【英文标题】:Mouse scroll not working in a scroll viewer with a wpf datagrid and additional UI elements 【发布时间】:2011-02-24 17:22:33 【问题描述】:

我试图弄清楚如何让鼠标滚动在带有滚动查看器和数据网格的 wpf 窗口上工作。 WPF 和 C# 代码如下

<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">

            <Border Name="DataGridBorder" BorderThickness="2"  Margin="1" CornerRadius="4" BorderBrush="#FF080757">
                <dg:DataGrid AutoGenerateColumns="False" Name="ValuesDataGrid" 
                         BorderThickness="0" CanUserResizeColumns="True" FontWeight="Bold" HorizontalScrollBarVisibility="Auto" 
                         CanUserReorderColumns="False" IsReadOnly="True" IsTextSearchEnabled="True" AlternationCount="2"
                         SelectionMode="Extended" GridLinesVisibility="All"                
                         HeadersVisibility="Column" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="False" CanUserSortColumns="False"
                         RowDetailsVisibilityMode="Collapsed"  SelectedIndex="0"
                         RowStyle="StaticResource CognitiDataGridRowStyle"
                         >

                    <dg:DataGrid.Columns>
                        <dg:DataGridTemplateColumn Header="Title" >
                            <dg:DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Horizontal" >
                                        <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Text="Binding Path=Name" FontWeight="Normal"  />
                                    </StackPanel>
                                </DataTemplate>
                            </dg:DataGridTemplateColumn.CellTemplate>
                        </dg:DataGridTemplateColumn>
                    </dg:DataGrid.Columns>
                </dg:DataGrid>
            </Border>
        </Grid>
        <Button Grid.Row="1" Height="90" >hello world</Button>
    </Grid>
</ScrollViewer>

C#代码如下

 public partial class Window1 : Window
  
     public Window1()
     
        InitializeComponent();
        initialize();
      

    public void initialize()
    
        ObservableCollection<MyObject> testList = new ObservableCollection<MyObject>();

        for (int i = 0; i < 20; i++)
        
            MyObject my = new MyObject("jack " + i);
            testList.Add(my);
        

        ValuesDataGrid.ItemsSource = testList;



    


public class MyObject

    public string Name  get; set; 



    public MyObject(string name)
    
        Name = name;
    
   

我面临的问题是,当使用鼠标滚动时,它在按钮上方时可以正常工作,但是当我将鼠标指针移到网格上并尝试滚动时,什么也没有发生。我可以直接移动滚动查看器的滚动条。我仍然是 wpf 新手,因此对于如何让鼠标滚动在数据网格上工作的任何帮助将不胜感激。我猜应该有一个非常简单的解决方案,但我无法弄清楚

【问题讨论】:

我遇到了完全相同的问题,但是下面的解决方案在 MVVM 应用程序中效果不佳。我们的架构师不需要视图中的任何代码,只需要 Xaml 或 Behaviors。 嗨 Kolky,我知道这个答案对你来说可能为时已晚,现在已经快 6 年了,但也许它对处于类似情况的其他人有用。通常,像这样的 UI 类型逻辑使用 MVVM 模式是可以接受的,例如使用类后面的代码。这是 MSDN 文章的链接:msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx 【参考方案1】:

我假设 DataGrid 不需要滚动 - 在 DataGrid 上设置 VerticalScrollBar="None"。

DataGrid 吞下鼠标滚动事件。

我发现是使用 PreviewMouseWheel 事件来滚动要滚动的容器。您需要命名滚动查看器以更改垂直偏移量。

private void DataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    
       scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset-e.Delta);
    

【讨论】:

【参考方案2】:

我认为 Dave 的解决方案是一个很好的解决方案。但是,我提出的一项建议是在滚动查看器上而不是在数据网格上捕获 PreviewMouseWheel 事件。如果不这样做,您可能会注意到一些细微差别,具体取决于您是在数据网格上滚动还是滚动条本身滚动。原因是当鼠标悬停在滚动条上时,scrollviewer 将处理滚动,而 datagrid 事件将在数据网格上处理滚动。例如,一个鼠标滚轮在数据网格上滚动可能会使您在列表中的位置比在滚动条上时更远。如果您在 scrollviewer 预览事件中捕捉到它,那么在滚动时都将使用相同的测量值。此外,如果您以这种方式捕获它,则不需要命名 scrollviewer 元素,因为您不需要对该对象的引用,因为您只需键入 cast 传递给 scrollviewer PreviewMouseWheel 事件的发送者对象。最后,我建议在事件结束时将事件标记为已处理,除非您出于某种原因需要在层次结构更靠后的元素中捕获它。示例如下:

private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    
        ScrollViewer scv = (ScrollViewer)sender;
        scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta);
        e.Handled = true;
    

【讨论】:

【参考方案3】:

我尝试了 Don B 的解决方案,它很好地解决了在没有其他内部可滚动控件的情况下滚动数据网格的问题。

对于滚动查看器具有其他可滚动控件和数据网格作为子项的情况,则要求该事件不应在主滚动查看器的事件处理程序末尾标记为已处理,因此它可能是在内部可滚动控件中也被捕获,但是它的副作用是,当滚动应该只发生在内部可滚动控件上时,它也会发生在主滚动查看器上。

因此,我更新了 Dave 的解决方案,在滚动查看器的查找方式上有所不同,因此无需知道滚动查看器的名称:

private void DataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)

    ScrollViewer scrollViewer = (((DependencyObject)sender).GetVisualParent<ScrollViewer>());
    scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);

【讨论】:

如果不依赖第三方库就更好了。 什么第三方库?这些是原生 WPF 控件。 GetVisualParent 不是。 PresentationCore中使用System.Windows.Media.VisualTreeHelper.GetParent(DependencyObject)怎么样【参考方案4】:

我发现 ScrollViewer 无法连接,这意味着如果它像您的情况一样连接,Grid 在 ScrollViewer 标记下开始,在 Grid 中我们有 DataGrid,在 DataGrid 中再次设置了 ScrollViewer 属性。即

  <Grid>
     <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition Height="45" />
        <RowDefinition Height="100*" />
        <RowDefinition Height="105" />
     </Grid.RowDefinitions>
     <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
     </Grid.ColumnDefinitions>

     <Label Grid.Row="0"
            Grid.Column="0"
            Margin="10,0,0,0"
            HorizontalAlignment="Left"
            VerticalAlignment="Center"
            FontWeight="Bold"
            Content="Vessel: " />
     <TextBox Height="30"
              Width="300"
              Margin="70,0,0,0"
              HorizontalAlignment="Left"
              BorderThickness="1,1,1,1"
              IsReadOnly="True"
              Name="txtVessel" />

     <Label  Grid.Row="0"
             Grid.Column="2"
             Margin="0,0,185,0"
             HorizontalAlignment="Right"
             VerticalAlignment="Center"
             FontWeight="Bold"
             Content="Month:" />

     <StackPanel Orientation="Horizontal"
                 Grid.Row="0"
                 Grid.Column="2"
                 Margin="0,0,0,0"
                 HorizontalAlignment="Right">

        <ComboBox BorderThickness="2"
                  HorizontalAlignment="Right"
                  Name="CmbMonth"
                  VerticalAlignment="Center"
                  Width="90" />
        <ComboBox BorderThickness="2"
                  HorizontalAlignment="Right"
                  Margin="5,0,0,0"
                  Name="CmbYear"
                  VerticalAlignment="Center"
                  Width="90" />

     </StackPanel>

     <Grid Grid.Row="1"
           Grid.ColumnSpan="2">
        <Grid.RowDefinitions>
           <RowDefinition Height="45" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
           <ColumnDefinition Width="30" />
           <ColumnDefinition Width="220" />
           <ColumnDefinition Width="80" />
           <ColumnDefinition Width="80" />
           <ColumnDefinition Width="80" />
           <ColumnDefinition Width="80" />
           <ColumnDefinition Width="120" />
           <ColumnDefinition Width="120" />
           <ColumnDefinition Width="140*" />
        </Grid.ColumnDefinitions>

        <Border BorderBrush="Black"
                BorderThickness="0,1,1,1"
                Grid.Row="0"
                Grid.Column="0" />
        <Border BorderBrush="Black"
                BorderThickness="0,1,1,1"
                Grid.Row="0"
                Grid.Column="1" />
        <Border BorderBrush="Black"
                BorderThickness="0,1,1,1"
                Grid.Row="0"
                Grid.Column="2" />
        <Border BorderBrush="Black"
                BorderThickness="0,1,1,1"
                Grid.Row="0"
                Grid.Column="3" />
        <Border BorderBrush="Black"
                BorderThickness="0,1,1,1"
                Grid.Row="0"
                Grid.Column="4" />
        <Border BorderBrush="Black"
                BorderThickness="0,1,1,1"
                Grid.Row="0"
                Grid.Column="5" />
        <Border BorderBrush="Black"
                BorderThickness="0,1,1,1"
                Grid.Row="0"
                Grid.Column="6" />
        <Border BorderBrush="Black"
                BorderThickness="0,1,1,1"
                Grid.Row="0"
                Grid.Column="7" />
        <Border BorderBrush="Black"
                BorderThickness="0,1,1,1"
                Grid.Row="0"
                Grid.Column="8" />

        <Label  Grid.Row="0"
                Grid.Column="1"
                Margin="0,0,0,0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontWeight="Bold"
                Content="Item" />

        <Label  Grid.Row="0"
                Grid.Column="2"
                Margin="0,0,0,0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontWeight="Bold"
                Content="Maker" />

        <Label  Grid.Row="0"
                Grid.Column="3"
                Margin="0,0,0,0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontWeight="Bold"
                Content="Model" />

        <Label  Grid.Row="0"
                Grid.Column="4"
                Margin="0,0,0,0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontWeight="Bold"
                Content=" Part No.&#x0a;Serial No." />

        <Label  Grid.Row="0"
                Grid.Column="5"
                Margin="0,0,0,0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontWeight="Bold"
                Content="Condition" />

        <Label  Grid.Row="0"
                Grid.Column="6"
                Margin="0,0,0,0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontWeight="Bold"
                Content="   Onboard&#x0a; Calibr/Test" />

        <Label  Grid.Row="0"
                Grid.Column="7"
                Margin="0,0,0,0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontWeight="Bold"
                Content="     Shore&#x0a; Callibration" />

        <Label  Grid.Row="0"
                Grid.Column="8"
                Margin="0,0,0,0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontWeight="Bold"
                Content="Remarks" />

     </Grid>

     <Border Grid.Row="2"
             Grid.ColumnSpan="2">
        <ScrollViewer Grid.Row="2"
                      Grid.ColumnSpan="2"
                      CanContentScroll="True"
                      HorizontalScrollBarVisibility="Disabled"
                      VerticalScrollBarVisibility="Auto"
                      Name="ScrollViewer3"
                      Margin="0,0,0,0">
           <Grid Name="grdOnBoardCalibrationRecord"
                 Margin="0,0,0,0">
              <Grid.ColumnDefinitions>
                 <ColumnDefinition Width="30"></ColumnDefinition>
                 <ColumnDefinition Width="220"></ColumnDefinition>
                 <ColumnDefinition Width="80"></ColumnDefinition>
                 <ColumnDefinition Width="80"></ColumnDefinition>
                 <ColumnDefinition Width="80"></ColumnDefinition>
                 <ColumnDefinition Width="80"></ColumnDefinition>
                 <ColumnDefinition Width="120"></ColumnDefinition>
                 <ColumnDefinition Width="120"></ColumnDefinition>
                 <ColumnDefinition Width="140*"></ColumnDefinition>
              </Grid.ColumnDefinitions>
              <Border Grid.Column="0"
                      BorderThickness="1,0,1,1"
                      BorderBrush="Black"
                      Grid.RowSpan="26"></Border>
              <Border Grid.Column="1"
                      BorderThickness="0,1,1,1"
                      Grid.RowSpan="26"></Border>
              <Border Grid.Column="2"
                      BorderThickness="0,1,1,1"
                      Grid.RowSpan="26"></Border>
              <Border Grid.Column="3"
                      BorderThickness="0,1,1,1"
                      Grid.RowSpan="26"></Border>
              <Border Grid.Column="4"
                      BorderThickness="0,1,1,1"
                      Grid.RowSpan="26"></Border>
              <Border Grid.Column="5"
                      BorderThickness="0,1,1,1"
                      Grid.RowSpan="26"></Border>
              <Border Grid.Column="6"
                      BorderThickness="0,1,1,1"
                      Grid.RowSpan="26"></Border>
              <Border Grid.Column="7"
                      BorderThickness="0,1,1,1"
                      Grid.RowSpan="26"></Border>
              <Border Grid.Column="8"
                      BorderThickness="0,1,1,1"
                      Grid.RowSpan="26"></Border>
           </Grid>
        </ScrollViewer>
     </Border>
     <Grid Grid.Row="3"
           Grid.ColumnSpan="2">
        <Grid.RowDefinitions>
           <RowDefinition Height="30"></RowDefinition>
           <RowDefinition Height="30"></RowDefinition>
           <RowDefinition Height="40"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
           <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <TextBox Grid.Row="0"
                 Grid.Column="0"
                 Height="30"
                 Width="300"
                 TextAlignment="Center"
                 Background="Gray"
                 IsReadOnly="True"
                 Margin="0,0,0,0"
                 HorizontalAlignment="Right"
                 VerticalAlignment="Bottom"
                 BorderThickness="1,1,1,1"
                 Name="txtChiefEngineer" />
        <Label  Grid.Row="1"
                Grid.Column="1"
                Margin="0,0,100,0"
                HorizontalAlignment="Right"
                VerticalAlignment="Center"
                FontWeight="Bold"
                Content="Chief Engineer" />
        <StackPanel Orientation="Horizontal"
                    Grid.Row="2"
                    Margin="0,0,0,0">
           <Label Name="lblonshorecomment"
                  Content=" Shore Comment : "
                  HorizontalAlignment="Center"
                  Margin="5,0,0,0"
                  FontWeight="Bold"
                  VerticalAlignment="Center"
                  FontFamily="Calibri"
                  FontStyle="Normal"
                  FontSize="14"></Label>
           <TextBox  BorderThickness="1"
                     FontWeight="Normal"
                     IsReadOnly="True"
                     Height="44"
                     Width="878"
                     TextWrapping="Wrap"
                     AcceptsReturn="True"
                     HorizontalAlignment="left"
                     Margin="0,0,0,0"
                     Name="txtShoreComment"
                     VerticalAlignment="Center" />
        </StackPanel>

     </Grid>
  </Grid>

【讨论】:

【参考方案5】:

要启用触摸支持,您可能还需要在 DataGrid 上将 ScrollViewer.PanningMode 设置为 None,并将相同的属性设置为 VerticalFirst 或*** ScrollViewer 上的其他值

示例

<ScrollViewer VerticalScrollBarVisibility="Auto" Margin="5" PanningMode="VerticalFirst">
    <DataGrid ScrollViewer.PanningMode="None" ItemsSource="Binding Items" />
</ScrollViewer>

当然,也可以使用 Don B 的回答中指出的 PreviewMouseWheel 事件来修复最初的鼠标滚动问题。

private static void ScrollViewer_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)

    var scrollViewer = (ScrollViewer)sender;
    scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);
    e.Handled = true;

或者,您可以简单地将以下附加属性设置为您的ScrollViewer

public class TopMouseScrollPriorityBehavior

    public static bool GetTopMouseScrollPriority(ScrollViewer obj)
    
        return (bool)obj.GetValue(TopMouseScrollPriorityProperty);
    

    public static void SetTopMouseScrollPriority(ScrollViewer obj, bool value)
    
        obj.SetValue(TopMouseScrollPriorityProperty, value);
    

    public static readonly DependencyProperty TopMouseScrollPriorityProperty =
        DependencyProperty.RegisterAttached("TopMouseScrollPriority", typeof(bool), typeof(TopMouseScrollPriorityBehavior), new PropertyMetadata(false, OnPropertyChanged));

    private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    
        var scrollViewer = d as ScrollViewer;
        if (scrollViewer == null)
            throw new InvalidOperationException($"nameof(TopMouseScrollPriorityBehavior).nameof(TopMouseScrollPriorityProperty) can only be applied to controls of type nameof(ScrollViewer)");
        if (e.NewValue == e.OldValue)
            return;
        if ((bool)e.NewValue)
            scrollViewer.PreviewMouseWheel += ScrollViewer_PreviewMouseWheel;
        else
            scrollViewer.PreviewMouseWheel -= ScrollViewer_PreviewMouseWheel;
    

    private static void ScrollViewer_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
    
        var scrollViewer = (ScrollViewer)sender;
        scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);
        e.Handled = true;
    

用法

<ScrollViewer b:TopMouseScrollPriorityBehavior.TopMouseScrollPriority="True" VerticalScrollBarVisibility="Auto" Margin="5" PanningMode="VerticalFirst">
    <DataGrid ScrollViewer.PanningMode="None" ItemsSource="Binding Items" />
</ScrollViewer>

其中 b: 是包含此行为的命名空间

这样你就不需要任何代码隐藏了,你的应用是纯粹的 MVVM

【讨论】:

【参考方案6】:

@fjch1997,我使用了您的解决方案,效果很好。 我发现只有一个问题。它与@Vadim Tofan 的评论有关:

对于滚动查看器具有其他可滚动的子项的情况 控件和数据网格,那么这要求事件不应该 在主事件处理程序的末尾标记为已处理 滚动查看器,因此它也可以在内部滚动中被捕获 控件,但是这有副作用,当滚动应该 只发生在内部可滚动控件上,它也会发生在 主滚动查看器。

我也尝试删除e.Handled = true 语句,但效果并不好——两个卷轴同时移动。所以,最后我将一点事件处理方法增强为以下方法:

private static void ScrollViewer_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)

    ScrollViewer scrollViewer = (ScrollViewer)sender;
    FrameworkElement origicalControlSender = e.OriginalSource as FrameworkElement;

    ScrollViewer closestScrollViewer = origicalControlSender.GetParent<ScrollViewer>();

    if (closestScrollViewer != null && !ReferenceEquals(closestScrollViewer, scrollViewer))
    
        return;
    

    scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);
    e.Handled = true;


public static T GetParent<T>(this FrameworkElement control)
    where T : DependencyObject

    FrameworkElement parentElement = control?.Parent as FrameworkElement;

    if (parentElement == null)
    
        return null;
    

    T parent = parentElement as T;

    if (parent != null)
    
        return parent;
    

    return GetParent<T>(parentElement);

如果存在内部 ScrollViewer,现在可以防止外部滚动条滚动。

【讨论】:

FrameworkElement 不包含来自GetParent() 方法的定义 @AlekseyTimoshchenko GetParent 在上面的这个答案中,它应该放在一个静态类中,因为它是一个扩展方法(这是由 this 关键字建议的)。如果this 有帮助,请告诉我。 是的,这很有帮助,tnx!【参考方案7】:

伙计们,我已经看到这里发布的大多数解决方案,基本上所有解决方案都是正确的,但我认为我们可以应用一种最简单、更优雅的语法。

假设我们不需要滚动数据网格,而是希望滚动 MainWindow

闲散的“戴夫”和“弗拉迪姆·托凡”回答

Private Void Scrlll(Object sebder, MouseWheelEventArgs e)

   var windows = (Window.GetWindow(this) as MainWindow).MainScroll;
   windows.ScrollToVerticalOffset(windows.VerticalOffset - e.Delta);

对不起,英语不好。

【讨论】:

【参考方案8】:

Don B 解决方案的一个改进是避免使用ScrollToVerticalOffset

scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta);

VerticalOffset - Delta 会带来非常不和谐的体验。 ScrollViewer 花了很多心思来使运动比这更平滑。我认为它还会根据 dpi 和其他因素缩小 delta...

我发现最好捕获并处理PreviewMouseWheelEvent 并将MouseWheelEvent 发送到预期的ScrollViewer。我的 Don B 解决方案版本如下所示。

private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)

    var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
    eventArg.RoutedEvent = UIElement.MouseWheelEvent;
    eventArg.Source = e.Source;

    ScrollViewer scv = (ScrollViewer)sender;
    scv.RaiseEvent(eventArg);
    e.Handled = true;

【讨论】:

【参考方案9】:

这是创建滚动 DataGrid 的 WPF 行为的更大示例。

首先定义以下基类,用于将框架元素与行为类及其行为实现粘合在一起。

using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Input;

namespace SomeAcme.Client.Infrastructure



    /// <summary>
    /// Behavior handler class for creating WPF behaviors.
    /// </summary>  
     [ExcludeFromCodeCoverage] 
    public class BehaviorHandler<TAssociatedObject, TBehaviorClass> 
        where TAssociatedObject: DependencyObject
        where TBehaviorClass : class, IAssociationBehavior, new()
    

        public BehaviorHandler()
        
        

        public static TBehaviorClass GetBehavior(DependencyObject obj)
        
            if (obj == null)
                return null; 

            return (TBehaviorClass)obj.GetValue(BehaviorProperty);
        

        public static void SetBehavior(DependencyObject obj, TBehaviorClass value)
        
            if (obj != null)
            
                obj.SetValue(BehaviorProperty, value);
            
        

        // Using a DependencyProperty as the backing store for Behavior.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BehaviorProperty =
            DependencyProperty.RegisterAttached("Behavior", typeof(TBehaviorClass), typeof(object), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits));


        public void FindOrCreateBehaviorOnDemand(DependencyObject dependencyObject)
        
             //Apply the behavior 
             TBehaviorClass behavior = FindOrCreateBehavior(dependencyObject);
             if (behavior != null)
             
                 dependencyObject.SetValue(BehaviorProperty, behavior); 
             
        

        public TBehaviorClass FindOrCreateBehavior(DependencyObject dependencyObject)
        
            if (dependencyObject == null)
                return null;
            var associatedObject = dependencyObject;
            if (associatedObject == null)
                return null;
            var behavior = dependencyObject.GetValue(BehaviorProperty) as TBehaviorClass;
            if (behavior == null)
            
                var behaviorAssociated = new TBehaviorClass();
                if (behaviorAssociated == null)
                    return null;
                behaviorAssociated.InitializeAssociation(associatedObject, AssociatedCommands);
                return behaviorAssociated;
            
            else
            
                return behavior;
            
         //TBehaviorClass FindOrCreateBehavior 

        /// <summary>
        /// Set the associated commands to pass into the WPF behavior, if desired
        /// </summary>
        public ICommand[] AssociatedCommands  get; set; 

    


这看起来有点复杂,但在将行为类与框架元素粘合在一起时,它简化了样板代码。在这种特殊情况下,我们不需要使用关联的命令。

我们的滚动查看器行为如下所示:

    using SomeAcme.Client.Infrastructure;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    namespace SomeAcme.Client.Infrastructure
    

        public static class ScrollViewerMouseWheelScrollBehavior
        


                public static string GetUseMouseScrollForScrollViewer(DependencyObject obj)
                
                    if (obj != null)
                    
                        return (string)obj.GetValue(UseMouseScrollForScrollViewerProperty);
                    
                    else
                        return null;
                

                public static void SetUseMouseScrollForScrollViewer(DependencyObject obj, string value)
                
                    if (obj != null)
                    
                        obj.SetValue(UseMouseScrollForScrollViewerProperty, value);
                    
                


 public static readonly DependencyProperty UseMouseScrollForScrollViewerCommandProperty =
                    DependencyProperty.RegisterAttached("UseScrollForScrollViewer", typeof(string), typeof(ScrollViewerMouseWheelScrollBehavior),
                        new PropertyMetadata(new PropertyChangedCallback(OnUseScrollForScrollViewerSet)));

                public static void OnUseScrollForScrollViewerSet(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
                
                    bool useScroll;
                    bool.TryParse(eventArgs.NewValue?.ToString(), out useScroll);
                    if (useScroll)
                    
                        var behaviorHandler =
                            new BehaviorHandler<ScrollViewer, ScrollViewerMouseScrollBehaviorImplementation>();
                        //behaviorHandler.AssociatedCommands = 
                        //    new Microsoft.Practices.Prism.Commands.DelegateCommand<object>[]  (Microsoft.Practices.Prism.Commands.DelegateCommand<object>)eventArgs.NewValue ;
                        behaviorHandler.FindOrCreateBehaviorOnDemand(dependencyObject);
                    
                

            

    

    namespace SomeAcme.Client.Infrastructure
    
        public class ScrollViewerMouseScrollBehaviorImplementation : IAssociationBehavior
        
            public void InitializeAssociation(DependencyObject associatedObject, params ICommand[] commands)
            
                //TODO: Add commands to associate
                var scrollViewer = associatedObject as ScrollViewer;
                if (scrollViewer != null)
                
                    scrollViewer.PreviewMouseWheel += ScrollViewer_PreviewMouseWheel;
                
            

            private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
            
                var scrollViewer = sender as ScrollViewer;
                if (scrollViewer != null)
                
                    scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);
                
            
        
    

在 XAML 中,我们可以先定义命名空间:

xmlns:local="clr-namespace:SomeAcme.Client.Infrastructure"
xmlns:sys="clr-namespace:System;assembly=mscorlib"

最后,我们准备好通过使用 XAML 中的 WPF 行为来使用鼠标滚轮滚动了。

..

 <TabControl Grid.Row="1">
        <TabItem Header="Skjemafelter">
            <ScrollViewer Height="700" local:ScrollViewerMouseWheelScrollBehavior.UseMouseScrollForScrollViewer="x:Static sys:Boolean.TrueString">
                <ListView x:Name="ColumnsListView" ItemsSource="Binding CurrentFields">
                    <ListView.View>
                        <GridView>

我已经测试并验证了这种方法有效。对于使用 WPF 应用程序的开发人员,利用 WPF 行为可以将代码中的代码量保持在最低限度,并忠实地使用 MVVM 方法。

【讨论】:

【参考方案10】:

Grid 有一个内置的 ScrollPanel,所以用 ScrollPanel 包裹它对我来说根本不起作用(制作固定高度的 Grid 是不可能的,因为我希望它可以很好地与应用程序的其余部分一起调整大小) .

我所做的是结合了这里的一些较高评价的解决方案,但基本上我的想法是摆脱 ScrollPanel 并将 DataGrid 的 PreviewMouseEvent 传递回实际处理滚动的父控件(在我的例子中,它是一个网格)。

    private void DataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    

        MouseWheelEventArgs eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
        
            RoutedEvent = MouseWheelEvent, Source = e.Source
        ;
        DependencyObject parent = VisualTreeHelper.GetParent((DependencyObject) sender);

        while (parent != null && !(parent is Grid))
        
            parent = VisualTreeHelper.GetParent(parent);
        

        if (parent != null)
        
            Grid grid = (Grid) parent;
            grid.RaiseEvent(eventArg);
        

        e.Handled = true;
    

【讨论】:

【参考方案11】:

我发现了这个:http://wpfthoughts.blogspot.com/2014/05/datagrid-vertical-scrolling-issues.html 并认为我应该在此处添加它。 “如果您的目标是 Framework 4.5,DataGrid 的内部 VirtualizingPanel 上有一个新的依赖对象,称为 ScrollUnit,可以设置为 Item(默认)或 Pixel。如果我们稍微修改 XAML,我们可以看到它是如何工作的。”

<DataGrid Name="DG" ItemsSource="Binding B0" AutoGenerateColumns="False" IsReadOnly="true" RowDetailsVisibilityMode="Visible" Width="200" 
      Height="100" VirtualizingPanel.ScrollUnit="Pixel">

【讨论】:

【参考方案12】:

我知道这已经有一段时间了,但我遇到了同样的问题并用 DockPanel 解决了它

<DockPanel Grid.ColumnSpan="3" LastChildFill="True">
    <Button DockPanel.Dock="Top" Content="Add" Grid.Column="3" HorizontalAlignment="Right" Width="110" Height="30" Margin="5"/>
    <DataGrid x:Name="xxDG" SelectionUnit="Cell" ItemsSource="Binding" Margin="0, 0, 0, 0" >
        ...   
    </DataGrid>
</DockPanel>

出于某种原因,这会像魅力一样处理鼠标滚轮事件。

【讨论】:

以上是关于鼠标滚动在带有 wpf 数据网格和其他 UI 元素的滚动查看器中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

如何在 WPF 数据网格上自动滚动

CSS 网格布局 - 仅针对部分内容的水平和垂直滚动

滚动后可拖动的jQuery UI不粘在网格上

如何在鼠标悬停时自定义 w2ui 网格工具提示

鼠标滚轮在功能区中的列表框上滚动 [WPF]

WPF 数据网格性能