Winforms ProgressBar занимает время для визуализации - PullRequest
2 голосов
/ 19 апреля 2011

Я заметил, что при использовании PorgressBar. Если установить значение x, отображаемое значение не будет немедленно обновлено, для его отрисовки потребуется небольшое количество времени, поскольку панель анимируется из текущего значения в новое значение.

Это легко увидеть в следующем коде:

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Label1.Text = ""
    Dim progressHandler = New Progress(Of Integer)(Sub(value) ProgressBar1.Value = value)
    Dim progress = CType(progressHandler, IProgress(Of Integer))
    Await Task.Run(Sub()
                       For i = 1 To 100
                           progress.Report(i)
                           Thread.Sleep(10)
                       Next
                   End Sub)
    Label1.Text = "Value Now at 100%"
    Await Task.Delay(650) 'it takes this long for the bar to be rendered
    Label1.Text += " - Finished drawing"
End Sub

При запуске этого кода вы заметите, что Value Now at 100% появляется задолго до того, как планка фактически достигнет 100%.

Есть ли какой-нибудь способ, которым я могу определить, когда бар закончил рендеринг?

Ответы [ 7 ]

3 голосов
/ 19 апреля 2011

Я только что попробовал это и могу точно понять, что вы имеете в виду. К сожалению, потратив немного времени, чтобы посмотреть, могут ли помочь функции DrawToBitmap на индикаторе выполнения, я ошибся.

Следующим шагом будет создание пользовательского индикатора выполнения, который отображает события после завершения рендеринга.

Для разумного примера того, как создать пользовательский индикатор выполнения, попробуйте здесь: http://msdn.microsoft.com/en-us/library/system.windows.forms.progressbarrenderer(v=VS.100).aspx

Быстрое сканирование по коду выглядит так, как будто вы должны иметь возможность подключить событие OnRendered или аналогичное к вызовам DrawHor horizontalChunks (или DrawVerticalChunks) или вокруг них.

Вероятно, не тот ответ, который вы получили после, но, по крайней мере, дает вам контроль, который вам нужен, если вы его преследуете?

Примечание: я сам не пробовал этого, поэтому, пожалуйста, не присылайте мне письма с ненавистью, если вы потратите на это целый день, чтобы найти те же результаты ...

Удачи!

EDIT:

Не был доволен моим ответом, мне показалось немного ленивым ... В следующем примере используется пользовательский индикатор выполнения, как я описал. Он имеет несколько основных свойств для установки максимальных / минимальных значений, выполнения шагов и прямой установки значения. Я проверил это, изменив интервал ожидания на разные значения, во всех случаях форма отображала индикатор выполнения как заполненный до закрытия. Обратите внимание на новое событие OnRendered.

Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Windows.Forms.VisualStyles

Public Class Form1
    Inherits Form
    Private WithEvents bar1 As ProgressBarWithRender = New ProgressBarWithRender()


    Public Sub New()
        InitializeComponent()
        Me.Size = New Size(500, 500)
        bar1.Location = New Point(100, 100)
        bar1.Width = 300
        bar1.Height = 50
        bar1.Maximum = 30
        bar1.Step = 1
        Controls.Add(bar1)
    End Sub

    Public Sub OnRendered(ByVal valueRendered As Integer) Handles bar1.OnRendered
        If valueRendered = bar1.Maximum Then
            ' We know everything has been drawn
            Me.Close()
        End If
    End Sub


    <STAThread()> _
    Public Shared Sub Main()
        ' The call to EnableVisualStyles below does not affect
        ' whether ProgressBarRenderer.IsSupported is true; as 
        ' long as visual styles are enabled by the operating system, 
        ' IsSupported is true.
        Application.EnableVisualStyles()
        Application.Run(New Form1())

    End Sub 'Main

    Private Sub Form1_Click(sender As Object, e As System.EventArgs) Handles Me.Click
        For i = 1 To 30
            bar1.PerformStep()
            Threading.Thread.Sleep(10)
        Next
    End Sub

End Class 'Form1

Public Class ProgressBarWithRender
    Inherits Control

    Public Delegate Sub RenderedEventArgs(ByVal valueRendered As Integer)
    Public Event OnRendered As RenderedEventArgs

    Private ProgressBarRectangles() As Rectangle

    Public Property [Step] As Integer

    Public Property InnerPadding As Integer = 3

    Private _Maximum As Integer
    Public Property Maximum As Integer
        Get
            Return _Maximum
        End Get
        Set(value As Integer)
            _Maximum = value
            CalculateTickSizes()
        End Set
    End Property

    Private _Minimum As Integer
    Public Property Minimum As Integer
        Get
            Return _Minimum
        End Get
        Set(value As Integer)
            _Minimum = value
            CalculateTickSizes()
        End Set
    End Property

    Private _Value As Integer
    Public Property Value As Integer
        Get
            Return _Value
        End Get
        Set(newValue As Integer)
            If newValue < Me.Value AndAlso newValue > 0 Then
                Throw New NotImplementedException("ProgressBarWithRender does not support decrementing the value")
            End If
            Me._Value = newValue
        End Set
    End Property

    Public Sub PerformStep()
        ' Ensure step doesn't exceed boundaries
        If Value + [Step] > Maximum Then
            Value = Maximum
        ElseIf Value + [Step] < Minimum Then
            Value = Minimum
        Else
            Value += [Step]
        End If

        ' We are limited by the Renderers Chunk Width, so we possibly can't draw every step if there is a high maximum
        Dim g As Graphics = Me.CreateGraphics
        ProgressBarRenderer.DrawHorizontalChunks(g, ProgressBarRectangles(Value - Minimum))
        RaiseEvent OnRendered(Value)

    End Sub

    Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
        If Not ProgressBarRenderer.IsSupported Then
            Throw New NotImplementedException("Progress Bar Rendering is not supported")
        End If
        ProgressBarRenderer.DrawHorizontalBar(e.Graphics, ClientRectangle)
    End Sub

    Private Sub CalculateTickSizes()
        ' Changing the Maximum will change the tick rectangle size
        ProgressBarRectangles = New Rectangle(Maximum) {}
        Dim chunkThickness As Integer = ProgressBarRenderer.ChunkThickness + (ProgressBarRenderer.ChunkSpaceThickness * 2)
        Dim tickThickness As Double = ((ClientRectangle.Width - (InnerPadding * 2)) - (ProgressBarRenderer.ChunkSpaceThickness * 2)) / (Maximum - Minimum)
        If tickThickness < chunkThickness Then
            Debug.Print("This will go wrong because we can't draw small enough chunks...")
        End If
        For i As Integer = 0 To Maximum
            Dim filledRectangle As Integer = CInt(tickThickness * i)
            ProgressBarRectangles(i) = New Rectangle(ClientRectangle.X + InnerPadding,
                                                     ClientRectangle.Y + InnerPadding,
                                                     filledRectangle,
                                                     ClientRectangle.Height - (InnerPadding * 2))
        Next
    End Sub

End Class
0 голосов
/ 25 апреля 2018

У меня хорошие результаты с запаздыванием прогресс-бара, установка значения с помощью этой функции:

Private Sub SetProgressNoAnimation(ByVal value As Integer)
    ' To get around the progressive animation, we need to move the 
    ' progress bar backwards.
    If (value = progressBarCarga.Maximum) Then
        ' Special case as value can't be set greater than Maximum.
        progressBarCarga.Maximum = (value + 1)
        ' Temporarily Increase Maximum
        progressBarCarga.Value = (value + 1)
        ' Move past
        progressBarCarga.Maximum = value
        ' Reset maximum
    Else
        progressBarCarga.Value = (value + 1)
        ' Move past
    End If
    progressBarCarga.Value = value
    ' Move to correct value
End Sub

Дополнительная информация:

https://derekwill.com/2014/06/24/combating-the-lag-of-the-winforms-progressbar/

0 голосов
/ 15 июля 2015

Я обнаружил, что использование PerformStep () вместо установки значения не имело этой задержки рендеринга - все равно пришлось вызывать Application.DoEvents ().

0 голосов
/ 17 августа 2013

Это мой код, основанный на предложении Мэтта Вилко:

Imports System.Net
Imports System.IO
Imports System.Text.RegularExpressions
Public Class Form1
Dim client As New WebClient
Public Sub New()

    ' This call is required by the Windows Form Designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.
    AddHandler client.DownloadStringCompleted, AddressOf client_DownloadStringCompleted
    AddHandler client.DownloadProgressChanged, AddressOf client_DownloadProgressChanged

End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    ProgressBar1.Value = 0
    ProgressBar1.Visible = True
    client.DownloadStringAsync(New Uri("http://somewebsite.com"), Nothing)
End Sub
Private Sub client_DownloadProgressChanged(ByVal sender As Object, ByVal e As System.Net.DownloadProgressChangedEventArgs)
    If ProgressBar1.Value < e.ProgressPercentage Then
        ProgressBar1.Value = e.ProgressPercentage
    End If
End Sub
Private Sub client_DownloadStringCompleted(ByVal sender As Object, ByVal e As System.Net.DownloadStringCompletedEventArgs)
    ProgressBar1.Value = 100
    Timer1.Enabled = True
End Sub

Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
    Static waitToCloseProgressBar As Integer
    If ProgressBar1.Value = 100 Then
        If waitToCloseProgressBar > 6 Then
            Timer1.Enabled = False
            waitToCloseProgressBar = 0
            ProgressBar1.Visible = False
        Else
            waitToCloseProgressBar = waitToCloseProgressBar + 1
        End If
    End If
End Sub
End Class
0 голосов
/ 26 октября 2011

Я получил приемлемый результат, вставив frmMain.Refresh
после присвоения нового значения стандартному ProgressBar.

0 голосов
/ 13 мая 2011

У меня похожая проблема.Я бы предпочел подать в суд на стандартный индикатор выполнения, чтобы иметь типичный дизайн в приложении.

Это правда, что для обновления требуется время, и, поскольку DoEvents не работает сам по себе, я бы рекомендовал выполнить загрузку с помощью фонового работника.Когда закончите, и это все еще не работает, тогда или добавьте doevents, или небольшую задержку.Но я думаю, что ваше решение для объявления с задержкой в ​​100 мс было бы лучшим, поскольку оно требует минимальных изменений и все еще работает.как насчет просто добавить 10 мс задержки к нему.Другой способ: попробуйте progressBar.Invalidate (принудительно перерисовать), добавьте код для рисования, который проверяет, выполняется ли рисование, а затем закройте форму.Я предполагаю, что DoEvents не будет работать в краске, так как краска должна закончиться.так что вы можете включить таймер с интервалом 100 мс, и он закроет окно.

обо всем, что меньше 1/24 секунды, невидимо для людей, но ... в глазу есть жидкости, которые видны даже корочевещи.Разница заключается в том, что мы можем немедленно реагировать на что-то, но поскольку информация «сжигается» на поверхности глаз до тех пор, пока она не будет «прочитана» нервами, она не будет потеряна.Есть даже проблемы с 60 Гц, и я уверен, что большинство людей знают, что раздражающая проблема 60 Гц crt.ЖК-дисплей не мерцает так сильно, но если вам нужна высокая частота кадров, а 60 недостаточно, то вы все равно можете «видеть» проблемы с 60, и он выглядит намного лучше при 100 или выше.

0 голосов
/ 19 апреля 2011

Проблема в том, что вы находитесь в однопоточной программе, и потоку нужно время для обновления дисплея.

Добавить строку

Application.DoEvents()

Перед закрытием подпункта UpdateProgress.

И вы можете избавиться от последних двух обновлений.

...