DataGrids over ScrollViewer prevent it to scroll
I have multiples DataGrids disposed over a ScrollViewer. These DataGrids have an "height: auto" property so that I can hide the scrollbar and view all the content. The only problem is that the DataGrids takes the focus and so I can't scroll the ScrollViewer. Is that a property to keep the focus on the ScrollViewer but also keeping th开发者_开发百科e behaviour of the DataGrids (so I can select elements) ?
Thank you !
It's to late, but I resolved this problem in this manner: I created the PreviewMouseWheel event for DataGrid and manually scrolled the wrapping ScrollViewer
private void dgInvoicesItems_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
this.scrInvoice.ScrollToVerticalOffset(this.scrInvoice.ContentVerticalOffset - e.Delta);
}
I ran across this exact same issue except my scenario was a little bit more complicated. Instead of having DataGrid in a ScrollViewer, I had a bunch of UserControl (called ProductDataGrid and defined below) in my ScrollViewer:
ProductDataGrid.xaml:
<UserControl x:Class="My.Control.ProductDataGrid" ...>
<Grid>
<Grid.RowDefinitions>...</Grid.RowDefinitions>
<TextBlock x:Name="Header" Grid.Row="0" ... />
<DataGrid x:Name="ProductData" Grid.Row="1" ... />
</Grid>
</UserControl>
ProductPortfolioListView.xaml:
<Page ...
xmlns:my="clr-namespace:My.Control"
....>
<Grid>
<Grid.RowDefinitions>...</Grid.RowDefinitions>
<ScrollViewer x:Name="ProductScrollViewer">
<StackPanel>
<my:ProductDataGrid ... />
<my:ProductDataGrid ... />
<my:ProductDataGrid ... />
</StackPanel>
</ScrollViewer>
The solution provided by Livsi is spot on correct but my UserControl did not have access to my ScrollViewer, so here is my solution:
ProductPortfolioListView.xaml:
<Page ...
xmlns:my="clr-namespace:My.Control"
....>
<Grid>
<Grid.RowDefinitions>...</Grid.RowDefinitions>
<ScrollViewer x:Name="ProductScrollViewer">
<StackPanel>
<my:ProductDataGrid ...
PreviewMouseWheel="ProductDataGrid_PreviewMouseWheel" />
<my:ProductDataGrid ...
PreviewMouseWheel="ProductDataGrid_PreviewMouseWheel" />
<my:ProductDataGrid ...
PreviewMouseWheel="ProductDataGrid_PreviewMouseWheel" />
</StackPanel>
</ScrollViewer>
ProductPortfolioListView.xaml.cs:
void ProductDataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs args)
{
ProductScrollViewer.ScrollToVerticalOffset(ProductScrollViewer.ContentVerticalOffset - args.Delta;
args.Handled = true;
}
Note the beauty of this solution lies in the fact that I can separate my DataGrid from the Page that will hold them, so I achieve code isolation as well as less duplicated code. And even better, I absolutely utilize the fact that RoutedEvents keep propragating from the Source to all of its parents until someone handles it (which in my case is my ProductScrollViewer).
TopMouseScrollPriorityBehavior.TopMouseScrollPriority
You can simply set the following Attached Property to your ScrollViewer
public class TopMouseScrollPriorityBehavior
{
public static bool GetTopMouseScrollPriority(DependencyObject obj)
{
return (bool)obj.GetValue(TopMouseScrollPriorityProperty);
}
public static void SetTopMouseScrollPriority(DependencyObject 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
Touch Support
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>
Try setting the CanContentScroll on the DataGrid to False like this:
<DataGrid ScrollViewer.CanContentScroll="False" ... />
精彩评论