开发者

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" ... />
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜