Ayuda con un Contador

Iniciado por Tomas1982, 26 Enero 2017, 16:00 PM

0 Miembros y 1 Visitante están viendo este tema.

Tomas1982

Estoy tratando de agregarle un contador a una copia, pero no e logrado de que este se me actualice sin la necesidad de actualizar el form. Esto es lo que tengo.

Código (vbnet) [Seleccionar]

for
File.Copy(RutaOrigen, RutaGF, True)
FCopy.Text = "1" + Val(FCopy.Text)
Me.Text = ListBox1.Items.Count.ToString() & ".Archivos"
Me.Refresh()
next
 

Al refrescar me parpadea el form.
La idea es que este me enumere la cantidad de ficheros copiados.
Saludos y gracias.

Eleкtro

#1
Cita de: Tomas1982 en 26 Enero 2017, 16:00 PMEstoy tratando de agregarle un contador a una copia

Hola. El problema que has planteado carece de la información necesaria, para empezar la sentencia del For está incompleta / no es compilable. Intenta ser más específico la próxima vez y al menos aportar un código/ejemplo funcional... no nos hagas asumir las cosas, esto es programación y requiere todos los detalles posibles por tu parte para evitar asunciones y preguntas recurrentes.




Cita de: Tomas1982 en 26 Enero 2017, 16:00 PMse me actualice sin la necesidad de actualizar el form.

Lo que ocurre es que estás realizando una operación "bloqueante" en el thread de la UI, es decir, estás ejecutando un búcle en el thread de la UI y hasta que el bloque del For no termine su ejecuión no podrás hacer nada más. Aparte de eso, refrescar el Form por completo es una operación expensiva y no hay necesidad de ello, en todo caso deberías refescar el Label/Control en el que necesites actualizar "X" información llamando al método Control.Update().

Con respecto al problema del bloqueo de la UI, la solución apropiada para llevar a cabo esto no es "hacer click y listo", se necesita un previo entendimiento y práctica por tu parte sobre la programación asincrónica en general. De todas formas te mostraré un ejemplo que puedes adaptar donde además te muestro el uso del paralelismo para acelerar la operación de copiado de archivos (o no. Depende, mientras no sean archivos de gran tamaño ok)...

En fin, lo que hace el siguiente código, aparte de evitar el bloqueo de la UI claro está, es realizar una copia NO-recursiva de los archivos del directorio "A" al directorio "B", y mostrar el progreso de archivos copiados en el control que le pasemos a la función, en este caso un label.

Código (vbnet) [Seleccionar]
Imports System.IO
Imports System.Threading

Public NotInheritable Class Form1 : Inherits Form

   Private Async Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
       Dim copyTask As Task(Of Integer) = Me.CopyFiles("C:\Source\", "C:\Destination\", Me.Label1)
       Await Task.WhenAll(copyTask)

       MessageBox.Show(String.Format("Files copied: {0}", copyTask.Result))
   End Sub

   Public Async Function CopyFiles(ByVal srcDirPath As String, ByVal dstDirPath As String, ByVal progressCtrl As Control) As Task(Of Integer)

       Dim files As IEnumerable(Of FileInfo) = New DirectoryInfo(srcDirPath).EnumerateFiles("*", SearchOption.TopDirectoryOnly)
       Dim maxFileCount As Integer = files.Count()
       Dim curFileCount As Integer

       Dim updateLabelCallback As New SendOrPostCallback(
           Sub(ByVal state As Object)
               progressCtrl.Text = String.Format("{0} of {1} files copied...", CInt(state), maxFileCount)
           End Sub)

       Dim copySingleFileAction As New Action(Of FileInfo)(
           Sub(ByVal file As FileInfo)
               Try
                   file.CopyTo(Path.Combine(dstDirPath, file.Name), overwrite:=True)
                   SynchronizationContext.Current.Post(updateLabelCallback, Interlocked.Increment(curFileCount))
               Catch ex As Exception
               End Try
           End Sub)

       Dim copyAllFilesFunc As Func(Of Integer) =
           Function() As Integer
               Parallel.ForEach(Of FileInfo)(files, copySingleFileAction)
               Return curFileCount ' Return the amount of files copied.
           End Function

       If Not New DirectoryInfo(dstDirPath).Exists Then
           Directory.CreateDirectory(dstDirPath, Nothing)
       End If

       Dim t As New Task(Of Integer)(copyAllFilesFunc, TaskCreationOptions.LongRunning)
       t.Start()
       Await t

       Return t.Result
   End Function

End Class


Si por el momento este tipo de solución asincrónica te resultase incomprensible, entonces siempre puedes recurrir a llamar al método Application.DoEvents() en el For que has compartido al principio, y listo, pero te advierto que hacer eso es programación irresponsable y deberías evitarlo a toda costa por varios motivos que no viene al caso explicar ahora (a menos que tengas esa duda).

Ejemplo:
Código (vbnet) [Seleccionar]
Dim counter As Integer

for ...
    File.Copy(RutaOrigen, RutaGF, overwrite:=True)
    FCopy.Text = CStr(Threading.Thread.Interlocked.Increment(counter))
    ' Descomentar en caso de que el valor no se actualice correctamente:
    ' FCopy.Update()
    Application.DoEvents()
next


Saludos!








Tomas1982

Eleкtro: Evidentemente ese no es el código, simplemente quise poner una idea de lo que quería, como lo explique. El código completo donde realizo la copia es este.

Código (vbnet) [Seleccionar]


Private Sub Organizar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Organizar.Click

        If TextBox1.Text = "" Then
            MessageBox.Show("Seleccionar ruta origen", "Ruta...")
        ElseIf TextBoxDestino.Text = "" Then
            MessageBox.Show("Seleccione ruta Destino", "Ruta...")
        End If
        'Recorre el Listbox item por item
        Try
            For i As Integer = 0 To Me.ListBox1.Items.Count - 1
                RutaOrigen = ListBox1.Items(i)
                'Obtener el nombre del fichero
                NDFichero = Path.GetFileName(RutaOrigen)
                'Obtener la extensión del fichero
                ExtFichero = Path.GetExtension(RutaOrigen)
                'Le quito el punto para usarlo en el nombre de la carpeta
                Dim MyChar() As Char = {"."}
                NCConExt = ExtFichero.TrimStart(MyChar)
                Console.WriteLine(NCConExt)
                'Crea el directorio si no existe
                If Not Directory.Exists(RutaDestino & "\" & NCConExt & "\") Then
                    Directory.CreateDirectory(RutaDestino & "\" & NCConExt & "\")
                End If
                'Prepara la ruta mas el fichero a copiar
                RDestino = RutaDestino & "\" & NCConExt & "\" & ExtFichero
                'Try

                If System.IO.File.Exists(RDestino) Then
                    '<<El archivo a copiar ya existe en destino>>
                    'Obtiene nombre del archivo sin extensión
                    Dim sFileName As String = ExtFichero.Substring(0, ExtFichero.Length - ExtFichero.ToString.Length)
                    Dim num As Integer = Nothing
                    While System.IO.File.Exists(RDestino) 'Cambia el
                        num += 1
                        'Renombra archivo en destino. Ej: C:\Organizado\ext\file(1).ext
                        RDestino = String.Format("{0}{1}({2}){3}", RutaDestino & "\" & NCConExt & "\", sFileName, num, ExtFichero)
                    End While
                End If
                Try
                    If RadioButton1.Checked = True Then
                        File.Copy(CStr(RutaOrigen), RDestino, False)
                    Else
                        File.Move(CStr(RutaOrigen), RDestino)
                    End If
                    'Muestra el fichero que se esta copiando
                    RutDFichero.Text = RutaOrigen
                    RutDFichero.Update()
                Catch ex As Exception
                    MsgBox("Error: " & ex.Message)
                End Try
                FCopy.Text = "1" + Val(FCopy.Text)
                FCopy.Update()
            Next
        Catch ex As Exception
            MsgBox("No se realizó la operación por: " & ex.Message)
        End Try
        Dim result As Integer = MessageBox.Show("Operación terminada> Deseas limpiar los datos", "caption", MessageBoxButtons.YesNo)
        If result = DialogResult.No Then
            Exit Sub
        ElseIf result = DialogResult.Yes Then
            Limpiardatos()
        End If
    End Sub

   

Darle una revisada para ver que me va mal y corregirlo. Saludossss

Eleкtro

#3
Cita de: Tomas1982 en 31 Enero 2017, 20:06 PMDarle una revisada para ver que me va mal y corregirlo.

Hola.

Aquí no se aceptan órdenes, ni tampoco le hacemos el trabajo a nadie.




Debo comentar algo, y es que ha habido una pequeña confusión. La primera vez que leí tu post donde explicaste el problema, por algún motivo me rallé (lo lei deprisa o algo, no se) y pensé que te referias a un problema de bloqueo de la UI, no a un problema de flickering, y te mostré dos soluciones diferentes enfocadas a evitar el bloqueo de la UI (que no el flickering), lo siento por eso. Lo bueno de esto es que de todas formas puedes aprovechar el primer ejemplo (el asincrónico) para adaptarlo y evitar llamar a "Me.Refresh()" ya que refrescar el Form/Todos los controles es el causante de lo que te ocurre. Empieza por leer a fondo la respuesta que te di donde digo que debes hacer en lugar de llamar a "Me.Refresh", y adaptar a tus necesidades aquél que mostré código. Ah, y para reducir (o en el mejor de los casos evitar) el flickering en WindowsForms siempre es bueno que actives la propiedad DoubleBuffered de tu Form.

Saludos.