【问题标题】:How to stop controls from flickering during MouseMove Event如何在 MouseMove 事件期间阻止控件闪烁
【发布时间】:2017-07-27 18:17:56
【问题描述】:

我知道这是一个非常受欢迎的问题,但我找到的解决方案都没有对我有用。

背景:我在 VS2015 中有一个 windows 窗体项目,它从文本文件中读取数据并将数据绘制为折线图上的多个系列。 Chart.MouseMove 事件找到离鼠标最近的点并在其周围画一个圆圈。圆圈在 Chart_Paint 事件中绘制

Private Sub crtLogView(sender As Object,e As PaintEventArgs) Handles crtLogView.Paint
       Dim whitePen as New Pne(Color.White,2)
       e.Graphics.DrawEllipse(whitePen,cir) '//cir is a Public Rectangle
End Sub

在图表上移动鼠标时,随机控件会闪烁,然后又会闪烁,这非常烦人。我在下面发布了 MouseMove 事件代码。

我尝试过的潜在解决方案:

  • 打开表单的 DoubleBuffered 属性,它什么都不做
  • 使用 Me.Invalidate() 和 Me.Update() 方法,不会移动圆圈
  • 使用 Chart.Invalidate() 和 Chart.Update() 方法,虽然有效,但速度很慢
  • 将以下代码添加到我的 Form_Load 例程中,这似乎没有任何作用

对此的任何帮助将不胜感激

Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Me.SetStyle(ControlStyles.DoubleBuffer, True)
Me.SetStyle(ControlStyles.UserPaint, True)

MouseMove 事件代码:

 Private Sub crtLogView_MouseMove(sender As Object, e As MouseEventArgs) Handles crtLogView.MouseMove

        '//Show data for closest point to cursor & draw circle around point
        Dim hResult As HitTestResult = crtLogView.HitTest(e.X, e.Y)
        Dim srsNam As String = ""
        Dim mouseY As Single
        Dim pntDist As Double = 0
        Dim pntX As Single
        Dim pntY As Single
        Dim mouseX As Single

        On Error GoTo ErrTrap

        '//Get X-Axis Position as integer
        mouseX = Int(hResult.ChartArea.AxisX.PixelPositionToValue(e.X))
        '// Set time value
        lblTime.Text = String.Format("{0:n2}", hResult.ChartArea.AxisX.PixelPositionToValue(e.X) / 160)

        '//Get Y-Axis Position
        mouseY = hResult.ChartArea.AxisY.PixelPositionToValue(e.Y)

        '//Get distance from mouse to point on Series(0)
        pntDist = Math.Abs(crtLogView.Series(0).Points(mouseX).YValues(0) - mouseY)
        srsNam = crtLogView.Series(0).Name '//1st series name
        '//Find closest series
        For i As Integer = 1 To crtLogView.Series.Count - 1
            If Math.Abs(crtLogView.Series(i).Points(mouseX).YValues(0) - mouseY) < pntDist Then
                pntDist = Math.Abs(crtLogView.Series(i).Points(mouseX).YValues(0) - mouseY)
                srsNam = crtLogView.Series(i).Name
            End If
        Next

        '//Set Top/Left values for circle
        pntY = crtLogView.ChartAreas(0).AxisY.ValueToPixelPosition(crtLogView.Series(srsNam).Points(mouseX).YValues(0)) - 4
        pntX = crtLogView.ChartAreas(0).AxisX.ValueToPixelPosition(Val(mouseX)) - 4

        '//Move circle to closest point
        cir.Location = New Point(pntX, pntY)

        '//Refresh the form to move the circle

        '//This works, but takes 2+ seconds to take effect
        'crtLogView.Invalidate()
        'crtLogView.Update()

        '//This does not work
        'Me.Invalidate()
        'Me.Update()

        '//This works, but randomly makes other controls flash/flicker
        Me.Refresh() 
ErrTrap:
    End Sub

【问题讨论】:

  • 我不知道你为什么要使用 mousemove 事件——它会一直触发。您是否尝试过在鼠标移动时重置计时器,然后在停止时绘制圆圈?
  • 为此使用图表注释或数据点标签可能会更好,而不是执行自定义绘制。如果你需要一个例子,我可以提供一个。
  • @peterG 感谢您的建议!我没有尝试过计时器,但现在有,它只会使闪烁的频率稍微降低
  • @TnTinMn 一个例子将不胜感激!我尝试将图表注释用于其他内容,但无法正确显示它们。我开始尝试 DataPoint Label 方法,但无法清除标签以移动到下一个点,所以我最终得到了鼠标接近的所有点的标签。
  • 好吧,我的意思是:将计时器设置为一些小的经验选择值。每次 mousemove 事件触发时,重新启动计时器。当计时器超时时触发画圆,也就是说,我们正在尝试生成 mousemovestopped 事件。因此,鼠标移动时不会绘制任何内容,但只有在它停止时才会绘制。

标签: vb.net winforms charts graphics


【解决方案1】:

在 cmets 中,我提供了一个使用图表注释或数据点标签的示例,以替代自定义围绕鼠标光标下的点绘制一个圆圈,并将其包含在下面的代码中。但是,我意识到 DataPoint Marker 应该提供 OP 正在寻求的功能,并且可能是正确的解决方案。因此,该选项也包括在内。

注释是图表级别的图形,其中 DataPoint 标签和 DataPoint Marker 顾名思义与各个 DataPoints 相关联。可能会涉及到注释的适当大小,因为它们的大小被指定为图表区域维度的百分比。此示例不会尝试根据当前图表大小调整注释大小。

以下代码示例适用于 WinForm。在 VS 中,向 WinForm 项目添加一个新类,并用它替换自动生成的代码。将这个Form设置为启动Form。

Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports Charting = System.Windows.Forms.DataVisualization.Charting
Public Class ChartDemo : Inherits Form
    Const yMultiplyer As Double = 100.0

    Private rnd As Random

    Friend WithEvents chart As System.Windows.Forms.DataVisualization.Charting.Chart
    Friend WithEvents rbAnnotation As System.Windows.Forms.RadioButton
    Friend WithEvents rbDataLabel As System.Windows.Forms.RadioButton
    Friend WithEvents rbMarker As System.Windows.Forms.RadioButton

    Private lastPoint As Charting.DataPoint
    Private ellispeAnnotation As Charting.EllipseAnnotation

    Public Sub New()
        InitializeComponent()
        rnd = New Random(0) ' use same basis for each run
        SetupChart()
    End Sub

    Private Sub InitializeComponent()
        Me.chart = New System.Windows.Forms.DataVisualization.Charting.Chart()
        Me.rbAnnotation = New System.Windows.Forms.RadioButton()
        Me.rbDataLabel = New System.Windows.Forms.RadioButton()
        Me.rbMarker = New System.Windows.Forms.RadioButton()
        CType(Me.chart, System.ComponentModel.ISupportInitialize).BeginInit()
        Me.SuspendLayout()

        Me.chart.Anchor = AnchorStyles.Top Or
                                    AnchorStyles.Bottom Or
                                    AnchorStyles.Left Or
                                    AnchorStyles.Right

        Me.chart.Location = New Point(4, 50)
        Me.chart.Size = New Size(600, 500)

        Me.rbAnnotation.AutoSize = True
        Me.rbAnnotation.Location = New Point(50, 10)
        Me.rbAnnotation.TabIndex = 1
        Me.rbAnnotation.Text = "Use Annotation"
        Me.rbAnnotation.UseVisualStyleBackColor = True

        Me.rbDataLabel.AutoSize = True
        Me.rbDataLabel.Location = New Point(200, 10)
        Me.rbDataLabel.TabIndex = 2
        Me.rbDataLabel.Text = "Use Data Label"
        Me.rbDataLabel.UseVisualStyleBackColor = True

        Me.rbMarker.AutoSize = True
        Me.rbMarker.Location = New Point(400, 10)
        Me.rbMarker.TabIndex = 3
        Me.rbMarker.Text = "Use Data Marker"
        Me.rbMarker.UseVisualStyleBackColor = True
        Me.rbMarker.Checked = True

        Me.AutoScaleDimensions = New SizeF(96.0!, 96.0!)
        Me.AutoScaleMode = AutoScaleMode.Dpi
        Me.ClientSize = New Size(610, 555)
        Me.Controls.AddRange({Me.rbDataLabel, Me.rbAnnotation, Me.rbMarker, Me.chart})
        Me.Text = "Charting Demo"
        CType(Me.chart, System.ComponentModel.ISupportInitialize).EndInit()
        Me.ResumeLayout(False)
        Me.PerformLayout()
    End Sub

    Private Sub SetupChart()
        chart.ChartAreas.Clear()
        chart.Legends.Clear()
        chart.Series.Clear()
        chart.Annotations.Clear()

        Dim area1 As New Charting.ChartArea("Area1")
        chart.ChartAreas.Add(area1)

        Dim ser As Charting.Series = chart.Series.Add("Series1")
        ser.ChartArea = area1.Name
        ser.ChartType = Charting.SeriesChartType.Line

        ' define defaults for point DataLabels
        ser.LabelBorderColor = Color.Red
        ser.LabelBorderWidth = 1
        ser.LabelBackColor = Color.WhiteSmoke
        ser.LabelForeColor = Color.Black

        ' define defaults for point DataMarkers
        ser.MarkerSize = 10
        ser.MarkerBorderWidth = 3
        ser.MarkerBorderColor = Color.Red
        ser.MarkerColor = Color.Transparent

        ' points for demo chart
        For x As Double = -5.0 To 5.0
            ser.Points.AddXY(x, rnd.NextDouble * yMultiplyer)
        Next

        ellispeAnnotation = CreateEllipseAnnotation()
        ellispeAnnotation.Visible = False
        chart.Annotations.Add(ellispeAnnotation)

    End Sub

    Private Sub chart_MouseLeave(sender As Object, e As EventArgs) Handles chart.MouseLeave
       ellispeAnnotation.Visible = False
       ClearLastPointDataLabel()
       ClearLastPointMarker()
    End Sub

    Private Function CreateEllipseAnnotation() As Charting.EllipseAnnotation
        Dim ret As New Charting.EllipseAnnotation()
        ret.ForeColor = Color.Black
        ret.Font = New Font("Arial", 10)
        ret.LineWidth = 2
        ret.Height = 7.5    ' % ChartArea height
        ret.Width = 15      ' % ChartArea width
        ret.BackColor = Color.PaleGoldenrod
        ret.LineDashStyle = Charting.ChartDashStyle.Solid
        Return ret
    End Function

    Private Sub chart_MouseMove(sender As Object, e As MouseEventArgs) Handles chart.MouseMove
        Dim htr As Charting.HitTestResult = chart.HitTest(e.X, e.Y)

        If htr.ChartElementType = Charting.ChartElementType.DataPoint Then
            Dim pt As Charting.DataPoint = DirectCast(htr.Object, Charting.DataPoint)
            If pt IsNot lastPoint Then
                SetDataPointLabel(pt)
                SetDataPointAnnotation(pt)
                SetDataPointMarker(pt)
                lastPoint = pt
            End If
        End If
    End Sub

    Private Sub SetDataPointAnnotation(pt As Charting.DataPoint)
        If rbAnnotation.Checked Then
            ellispeAnnotation.AnchorDataPoint = pt
            ellispeAnnotation.Text = String.Format("{0:N2}, {1:N2}", pt.XValue, pt.YValues(0))
            ellispeAnnotation.Visible = True
        End If
    End Sub

    Private Sub SetDataPointLabel(pt As Charting.DataPoint)
        ClearLastPointDataLabel()
        If rbDataLabel.Checked Then
            pt.Label = "#VALX{N2}, #VALY{N2}" ' case sensative, use uppercase for #VALX, #VALY
            pt.IsValueShownAsLabel = True
        End If
    End Sub

    Private Sub ClearLastPointDataLabel()
        If lastPoint IsNot Nothing Then
            lastPoint.Label = String.Empty
            lastPoint.IsValueShownAsLabel = False
        End If
    End Sub

    Private Sub SetDataPointMarker(pt As Charting.DataPoint)
        ClearLastPointMarker()
        If rbMarker.Checked Then pt.MarkerStyle = Charting.MarkerStyle.Circle
    End Sub

    Private Sub ClearLastPointMarker()
        If lastPoint IsNot Nothing Then
            lastPoint.MarkerStyle = Charting.MarkerStyle.None
        End If
    End Sub

    Private Sub rbAnnotation_CheckedChanged(sender As Object, e As EventArgs) Handles rbAnnotation.CheckedChanged
        If Not rbAnnotation.Checked Then
            ellispeAnnotation.Visible = False
        End If
    End Sub

    Private Sub rbDataLabel_CheckedChanged(sender As Object, e As EventArgs) Handles rbDataLabel.CheckedChanged
        ClearLastPointDataLabel()
    End Sub

    Private Sub rbMarker_CheckedChanged(sender As Object, e As EventArgs) Handles rbMarker.CheckedChanged
        ClearLastPointMarker()
    End Sub
End Class

【讨论】:

    猜你喜欢
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-14
    • 2013-02-24
    相关资源
    最近更新 更多