I am trying to figure out how to get the mouse scroll working on a wpf window with a scrollviewer and a datagrid within it. The WPF and C# code is below
<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>
and the C# code is as follows
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;
}
}
The problem i am facing is that when using the mouse to scroll, it works fine when it is over the button but as soon as i move the mouse pointer over the grid and try to scroll, nothing happens. I am able to move the scrollbar of the scrollviewer directly though. I am still a wpf novice so any help on how to get the mouse scroll to work over the datagrid would be appreciated. I am guessing there should be a pretty easy solution for this but I havent been able to figure it out
How to enable scrollbar in a WPF TextBox. The simplest way to add scrolling functionality to a TextBox control is by enabling its horizontal and vertical scrolling. The HorizontalScrollBarVisibility and VerticalScrollBarVisibility properties are used to set horizontal and vertical scroll bars of a TextBox.
There are two predefined elements that enable scrolling in WPF applications: ScrollBar and ScrollViewer. The ScrollViewer control encapsulates horizontal and vertical ScrollBar elements and a content container (such as a Panel element) in order to display other visible elements in a scrollable area.
In order to enable horizontal and/or vertical scrollbars you need to set the ScrollViewer's attached properties HorizontalScrollBarVisibility and/or VerticalScrollBarVisibility.
I think Dave's solution is a good one. However, one recommendation I'd make is to catch the PreviewMouseWheel event on the scrollviewer instead of on the datagrid. If you don't, you might notice some minor differences based on whether you're scrolling over the datagrid or the scroll bar itself. The reasoning is that the scrollviewer will be handling scrolling when the mouse is hovered over the scrollbar, and the datagrid event will handle the scrolling when over the datagrid. For instance, one mouse wheel scroll over the datagrid might bring you farther down your list then it would when over the scroll bar. If you catch it on scrollviewer preview event, all will use the same measurement when scrolling. Also, if you catch it this way, you won't need to name the scrollviewer element, as you won't need a reference to the object since you can just type cast the sender object passed into the scrollviewer PreviewMouseWheel event. Lastly, I'd recommend marking the event handled at the end of the event, unless you need to catch it in an element further down the heirarchy for some reason. Example below:
private void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
ScrollViewer scv = (ScrollViewer)sender;
scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta);
e.Handled = true;
}
I'm assuming the DataGrid does not need to scroll - Set the VerticalScrollBar="None" on the DataGrid.
The DataGrid swallows the mouse scroll event.
What I found is to use the PreviewMouseWheel event to scroll the container that you want to scroll. You will need to name the scrollviewer to change the Vertical offset.
private void DataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset-e.Delta);
}
An improvement to Don B's solution is to avoid using ScrollToVerticalOffset
.
scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta);
VerticalOffset - Delta
results in a pretty jarring experience. The ScrollViewer puts a lot of thought into making the movement smoother than that. I think it also scales the delta down based on dpi and other factors...
I found it's better to catch and handle the PreviewMouseWheelEvent
and send a MouseWheelEvent
to the intended ScrollViewer
. My version of Don B's solution looks like this.
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;
}
To enable touch support you might also want to set ScrollViewer.PanningMode
to None
on your DataGrid
and set the same property to VerticalFirst
or other value on your top level ScrollViewer
Example
<ScrollViewer VerticalScrollBarVisibility="Auto" Margin="5" PanningMode="VerticalFirst">
<DataGrid ScrollViewer.PanningMode="None" ItemsSource="{Binding Items}" />
</ScrollViewer>
Of course, also use the PreviewMouseWheel
event as indicated by Don B's answers to fix the original mouse scrolling issue.
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;
}
Or, you can simply set the following Attached Property to your 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;
}
}
Usage
<ScrollViewer b:TopMouseScrollPriorityBehavior.TopMouseScrollPriority="True" VerticalScrollBarVisibility="Auto" Margin="5" PanningMode="VerticalFirst">
<DataGrid ScrollViewer.PanningMode="None" ItemsSource="{Binding Items}" />
</ScrollViewer>
Where b: is the namespace that contains this behavior
This way your no code-behind is needed and your app is purely MVVM
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With