WinForms ReportViewer:鼠标滚轮滚动两页而不是一页

时间:2016-01-26 09:37:46

标签: c# winforms rdlc reportviewer

我在ReportViewer应用程序中使用WinForms控件来显示RDLC报告。

表单非常简单(它只包含reportviewer,没有其他内容),通常效果很好。

但是有一个恼人的问题:当用户使用鼠标滚轮更改页面时,每个向上/向下滚动向后/向后翻页,而不是一页

这使得阅读报告非常烦人,当然。为什么这样做,如何让它只滚动一页呢?

编辑:通过进一步测试,我可以确认ReportViewer的{​​{1}}事件为滚轮的每个“勾号”触发两次。还是不知道为什么......

4 个答案:

答案 0 :(得分:1)

这是一个非常有趣的问题。

ReportViewer的滚动触发器可能会使用来自正常displaymode的分页,无论它是如何定义的。在此displaymode中,记者只会在明确定义时添加分页符,或者添加由InteractiveSize属性确定的软分页符。

您可以将InteractiveSize属性设置为与PageSize相同,这样您就可以使用DisplayMode.Normal并保持与DisplayMode.PrintLayout相同的分页功能

然而,这可能仍然无法解决滚动问题,因为ReportViewer滚动也可能忽略软分页符。这意味着它首先在单个页面上滚动,然后应用分页,从而可以跳过页面。
如果是这种情况,那么我相信您唯一的另一个选择是编写自己的ReportViewer自定义版本并尝试修复它。

修改 事实证明滚动行为对我来说是正常的。这意味着它必须是特定于您的环境的问题。您使用的是哪个版本和框架?

您可以通过捕捉滚动事件并自行触发PageNavigation来轻松解决此问题。

答案 1 :(得分:1)

这是我的解决方案,在VB中。它也可以与按钮导航一起使用。

首先,我们必须将处理程序添加到报表查看器的导航按钮中。 我的控制报表查看器的名称是“ reportViewer

Private Sub Print_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
        Dim ts As ToolStrip = TryCast(reportViewer.Controls.Find("toolstrip1", True).First, ToolStrip)
        If Not ts Is Nothing Then
            For Each tsb As ToolStripItem In ts.Items
                If TypeOf tsb Is ToolStripButton AndAlso
                (TryCast(tsb, ToolStripButton).Name = "firstPage" Or
                TryCast(tsb, ToolStripButton).Name = "lastPage" Or
                TryCast(tsb, ToolStripButton).Name = "previousPage" Or
                TryCast(tsb, ToolStripButton).Name = "nextPage") Then
                    AddHandler tsb.Click, AddressOf tsb_Click
                End If
            Next
        End If
End Sub

然后是核心

Dim scrolled As New List(Of Integer)

Private Sub reportViewer_PageNavigation(sender As Object, e As PageNavigationEventArgs) Handles reportViewer.PageNavigation
    scrolled.Add(e.NewPage)
End Sub

Private Sub reportViewer_MouseWheel(sender As Object, e As MouseEventArgs) Handles reportViewer.MouseWheel
    If scrolled.Count > 1 Then
        reportViewer.CurrentPage = scrolled.Item(scrolled.Count - 2)
        scrolled.Clear()
    ElseIf scrolled.Count = 1 Then
        reportViewer.CurrentPage = scrolled.Item(scrolled.Count - 1)
        scrolled.Clear()
    End If
End Sub

Public Sub tsb_Click(ByVal sender As Object, ByVal e As EventArgs)
    scrolled.Clear()
End Sub

希望这会有所帮助

答案 2 :(得分:1)

编辑:仅当您的应用程序以32位运行时,原始堆栈跟踪才显示为true。对于64位,第二个调用中只有三行没有出现在第一行中,此处列出。

at System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at System.Windows.Forms.UnsafeNativeMethods.CallWindowProc(IntPtr wndProc, IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at System.Windows.Forms.NativeWindow.DefWndProc(Message& m)

处理这两种情况的更新代码是

private void Viewer_PageNavigation(object sender, PageNavigationEventArgs e) => e.Cancel = Environment.StackTrace.Contains("DefWndProc");

这仍然是一个丑陋的骇客,但确实有效。


要查看实际情况,您需要禁用“ Just My Code”并调试到Framework中。通过订阅ReportViewer.PageNavigation事件并设置断点,您可以看到导航发生了两次。第一次是由WM_MOUSEWHEEL的一个称为ReportViewer的组件处理的RenderingPanel消息触发的。该组件的OnMouseWheel方法仅调用ReportPanel.OnMouseWheel,它执行滚动/导航。但是,相同的窗口消息随后传递到ReportPanel,导致ReportPanel.OnMouseWheel被再次调用。本质上,每个低级滚轮消息都会导致两次滚动。您可以通过在第一次通话中插入ReportPanel.OnMouseWheel并设置e.Handled = true来进行测试。在这种情况下,不会再次调用ReportPanel.OnMouseWheel

请注意,我们正在谈论ReportViewer内部组件的鼠标滚轮事件,这与控件的滚轮事件不同。尽管具有所有内部功能,ReportViewer.MouseWheel仅引发一次,并且在两次PageNavigation事件之后都会发生。

最重要的是,控件始终将两次Wheel消息处理两次。通常,您不会注意到这一点。如果向下滚动并且报表滚动10%,您怎么知道它只应该滚动5%但实际上滚动了两次?但是,如果您的缩放级别足以使单个滚轮滚动等于整页,则...动臂...每个滚轮滚动两页。但是,我还没有确定为什么这只是打印预览模式下的问题。

没有干净的解决方案。我想到的最好的办法是检查ReportViewer.PageNavigation内的堆栈,如果事件是由RenderingPanel引起的,则忽略它。这样的修复看起来像这样:

/* The ReportViewer control is composed of several sub-controls. One of those is a RenderingPanel, whose OnMouseWheel method gets called
 * by the Framework in response to a window message. That method simply calls ReportPanel.OnMouseWheel, which performs scroll/navigation.
 * However, ReportPanel.OnMouseWheel is subsequently called again by the Framework in response to the same window message resulting in all
 * wheel events being processed twice. While this results in each wheel scroll being twice as large as it should be, it is generally not
 * noticeable (how would you know that the scroll distance should be half of what it is?) unless the zoom level is sufficient (i.e. Whole Page)
 * that a single wheel event results in an entire page navigation. In this case, two pages are flipped instead of one. In order to avoid this,
 * we simply cancel any page navigation that was caused by the RenderingPanel's handling of the wheel event. */    
private void Viewer_PageNavigation(object sender, PageNavigationEventArgs e) => e.Cancel = Environment.StackTrace.Contains("ReportPanel.RenderingPanel.OnMouseWheel");

答案 3 :(得分:-1)

我有同样的问题。每次使用鼠标滚轮时,PageNavigation事件都会触发两次。

我决定自定义PageNavigation事件:

    bool scroll = true;

    private void ReportViewer_PageNavigation(object sender, PageNavigationEventArgs e)
    {
        if (!scroll)
            e.Cancel = true;

        scroll = !scroll;
    }