Foro de elhacker.net

Programación => .NET (C#, VB.NET, ASP) => Mensaje iniciado por: Tomas1982 en 26 Enero 2017, 16:00 pm



Título: Ayuda con un Contador
Publicado por: Tomas1982 en 26 Enero 2017, 16:00 pm
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
  1. for
  2. File.Copy(RutaOrigen, RutaGF, True)
  3. FCopy.Text = "1" + Val(FCopy.Text)
  4. Me.Text = ListBox1.Items.Count.ToString() & ".Archivos"
  5. Me.Refresh()
  6. next
  7.  
 

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


Título: Re: Ayuda con un Contador
Publicado por: Eleкtro en 28 Enero 2017, 20:44 pm
Estoy 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.



se 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
  1. Imports System.IO
  2. Imports System.Threading
  3.  
  4. Public NotInheritable Class Form1 : Inherits Form
  5.  
  6.    Private Async Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
  7.        Dim copyTask As Task(Of Integer) = Me.CopyFiles("C:\Source\", "C:\Destination\", Me.Label1)
  8.        Await Task.WhenAll(copyTask)
  9.  
  10.        MessageBox.Show(String.Format("Files copied: {0}", copyTask.Result))
  11.    End Sub
  12.  
  13.    Public Async Function CopyFiles(ByVal srcDirPath As String, ByVal dstDirPath As String, ByVal progressCtrl As Control) As Task(Of Integer)
  14.  
  15.        Dim files As IEnumerable(Of FileInfo) = New DirectoryInfo(srcDirPath).EnumerateFiles("*", SearchOption.TopDirectoryOnly)
  16.        Dim maxFileCount As Integer = files.Count()
  17.        Dim curFileCount As Integer
  18.  
  19.        Dim updateLabelCallback As New SendOrPostCallback(
  20.            Sub(ByVal state As Object)
  21.                progressCtrl.Text = String.Format("{0} of {1} files copied...", CInt(state), maxFileCount)
  22.            End Sub)
  23.  
  24.        Dim copySingleFileAction As New Action(Of FileInfo)(
  25.            Sub(ByVal file As FileInfo)
  26.                Try
  27.                    file.CopyTo(Path.Combine(dstDirPath, file.Name), overwrite:=True)
  28.                    SynchronizationContext.Current.Post(updateLabelCallback, Interlocked.Increment(curFileCount))
  29.                Catch ex As Exception
  30.                End Try
  31.            End Sub)
  32.  
  33.        Dim copyAllFilesFunc As Func(Of Integer) =
  34.            Function() As Integer
  35.                Parallel.ForEach(Of FileInfo)(files, copySingleFileAction)
  36.                Return curFileCount ' Return the amount of files copied.
  37.            End Function
  38.  
  39.        If Not New DirectoryInfo(dstDirPath).Exists Then
  40.            Directory.CreateDirectory(dstDirPath, Nothing)
  41.        End If
  42.  
  43.        Dim t As New Task(Of Integer)(copyAllFilesFunc, TaskCreationOptions.LongRunning)
  44.        t.Start()
  45.        Await t
  46.  
  47.        Return t.Result
  48.    End Function
  49.  
  50. 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
  1. Dim counter As Integer
  2.  
  3. for ...
  4.    File.Copy(RutaOrigen, RutaGF, overwrite:=True)
  5.    FCopy.Text = CStr(Threading.Thread.Interlocked.Increment(counter))
  6.    ' Descomentar en caso de que el valor no se actualice correctamente:
  7.    ' FCopy.Update()
  8.    Application.DoEvents()
  9. next

Saludos!


Título: Re: Ayuda con un Contador
Publicado por: Tomas1982 en 31 Enero 2017, 20:06 pm
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
  1.  
  2. Private Sub Organizar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Organizar.Click
  3.  
  4.        If TextBox1.Text = "" Then
  5.            MessageBox.Show("Seleccionar ruta origen", "Ruta...")
  6.        ElseIf TextBoxDestino.Text = "" Then
  7.            MessageBox.Show("Seleccione ruta Destino", "Ruta...")
  8.        End If
  9.        'Recorre el Listbox item por item
  10.        Try
  11.            For i As Integer = 0 To Me.ListBox1.Items.Count - 1
  12.                RutaOrigen = ListBox1.Items(i)
  13.                'Obtener el nombre del fichero
  14.                NDFichero = Path.GetFileName(RutaOrigen)
  15.                'Obtener la extensión del fichero
  16.                ExtFichero = Path.GetExtension(RutaOrigen)
  17.                'Le quito el punto para usarlo en el nombre de la carpeta
  18.                Dim MyChar() As Char = {"."}
  19.                NCConExt = ExtFichero.TrimStart(MyChar)
  20.                Console.WriteLine(NCConExt)
  21.                'Crea el directorio si no existe
  22.                If Not Directory.Exists(RutaDestino & "\" & NCConExt & "\") Then
  23.                    Directory.CreateDirectory(RutaDestino & "\" & NCConExt & "\")
  24.                End If
  25.                'Prepara la ruta mas el fichero a copiar
  26.                RDestino = RutaDestino & "\" & NCConExt & "\" & ExtFichero
  27.                'Try
  28.  
  29.                If System.IO.File.Exists(RDestino) Then
  30.                    '<<El archivo a copiar ya existe en destino>>
  31.                    'Obtiene nombre del archivo sin extensión
  32.                    Dim sFileName As String = ExtFichero.Substring(0, ExtFichero.Length - ExtFichero.ToString.Length)
  33.                    Dim num As Integer = Nothing
  34.                    While System.IO.File.Exists(RDestino) 'Cambia el
  35.                        num += 1
  36.                        'Renombra archivo en destino. Ej: C:\Organizado\ext\file(1).ext
  37.                        RDestino = String.Format("{0}{1}({2}){3}", RutaDestino & "\" & NCConExt & "\", sFileName, num, ExtFichero)
  38.                    End While
  39.                End If
  40.                Try
  41.                    If RadioButton1.Checked = True Then
  42.                        File.Copy(CStr(RutaOrigen), RDestino, False)
  43.                    Else
  44.                        File.Move(CStr(RutaOrigen), RDestino)
  45.                    End If
  46.                    'Muestra el fichero que se esta copiando
  47.                    RutDFichero.Text = RutaOrigen
  48.                    RutDFichero.Update()
  49.                Catch ex As Exception
  50.                    MsgBox("Error: " & ex.Message)
  51.                End Try
  52.                FCopy.Text = "1" + Val(FCopy.Text)
  53.                FCopy.Update()
  54.            Next
  55.        Catch ex As Exception
  56.            MsgBox("No se realizó la operación por: " & ex.Message)
  57.        End Try
  58.        Dim result As Integer = MessageBox.Show("Operación terminada> Deseas limpiar los datos", "caption", MessageBoxButtons.YesNo)
  59.        If result = DialogResult.No Then
  60.            Exit Sub
  61.        ElseIf result = DialogResult.Yes Then
  62.            Limpiardatos()
  63.        End If
  64.    End Sub
  65.  
  66.  
   

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


Título: Re: Ayuda con un Contador
Publicado por: Eleкtro en 1 Febrero 2017, 00:02 am
Darle 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.