none
Problemas al actualizar un Canvas RRS feed

  • Pregunta

  • Hola a todos!

    les comento en primer lugar como tengo el Canvas, se trata de un simple canvas que se muestra u oculta a través de un DataStateBehavior, estándo este último enlazado a un campo llamado "Mostrar", usando para todo ello el patrón MVVM. Dicho canvas tiene un TextBlock para mostrar información en texto.

    También tengo un botón enlazado mediante ICommand para cambiar el valor del campo "Mostrar".

    El código del campo es el que se puede ver abajo y el que controla el texto es similar pero usando string.

            private bool mostrar;
            public bool Mostrar
            {
                get { return mostrar; }
                set
                {
                    mostrar = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(thisnew PropertyChangedEventArgs("Mostrar"));
                    }
                }
            }        
            
            private string mensaje;
            public string Mensaje
            {
                get { return mensaje; }
                set
                {
                    mensaje = value;
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(thisnew PropertyChangedEventArgs("Mensaje"));
                    }
                }
            }

    Hasta aquí todo correcto, dicho canvas se muestra u oculta cuando hago clic sobre el botón y cambio el valor del campo "Mostrar", además de hacer más cosas dentro del método que lanza el comando. También modifica el texto actualizando el campo al que está enlazado, pero todo ello cuando finaliza el método al que está enlazado el comando.

    El problema me surge cuando quiero actualizar el estado del Canvas mientras se ejecuta el método al que está enlazado el botón, para que vaya mostrando estados que yo modifique del campo "Mensaje".

    En definitiva, que no actualiza el estado del canvas hasta que no termina de ejecutar el método, por mucho que modifique el campo "Mensaje".

    ¿Cómo puedo actualizar el canvas?.

    Por ahora he querido probar con this.Dispatcher.BeginInvoke pero no puedo ya que uso MVVM.


    • Editado Spaikers miércoles, 21 de marzo de 2012 17:20
    miércoles, 21 de marzo de 2012 17:18

Todas las respuestas

  • Hola Spaikers,

    El problema es que el método que ejecuta el código de tu comando se está ejecutando en el hilo principal de la aplicación y hasta que no termine no le das tiempo a la UI para refrescarse.

    Puedes poner todo el código de tu comando en un hilo secundario, usando una instancia de ThreadStart. Una vez hecho esto, cuando quieras asignar un nuevo valor a las propiedades Mostrar o Mensaje, tendrás que hacer uso del Dispatcher desde tu ViewModel:

    Dispatcher dispatcher = Application.Current.RootVisual.Dispatcher; 
    dispatcher.BeginInvoke(() => 
    {
         Mostrar = true;
         Mensaje = "Paso 1";
    });

    De esta forma debería dejarte actualizar la UX mientras sigues realizando operaciones en background en tu ThreadStart.
    (No he podido probarlo, te lo digo de memoria, disculpa si hay algún error)

    Un saludo y espero que te sirva!


    MCTS - .NET 4.0 Windows Applications
    MCTS - .NET 4.0 Data Access
    MCTS - .NET 4.0 Service Communication Applications
    MCPD - .NET 4.0 Windows Developer
    Visita mi Blog en Geeks.ms
    Sigueme en Twitter

    jueves, 22 de marzo de 2012 5:28
    Moderador
  • Me va pero no va, me explico:

    En la vista modelo tengo lo siguiente:

        class VMClase : INotifyPropertyChanged
        {
            Dispatcher dispatcher = Application.Current.RootVisual.Dispatcher;
     
            public ICommand CActivar { get; set; }
     
            private void Activar(object p)
            {
                new System.Threading.Thread(Comenzar);
            }
     
            private void Comenzar()
            {
                try
                {
                    dispatcher.BeginInvoke(() => { Mensaje = "ACTIVANDO"; Mostrar = true; });
                    Servicio WCF.......
                }
                catch (Exception err)
                {
                    MessageBox.Show(err.Message);
                }
                finally
                {
     
                }
            }
     
            public void serviceWCF(object sender, serviceWCFCompletedEventArgs e)
            {
                ........
                if (e.Result == true)
                {
                    guardar();
                }
            }
     
            private void guardar()
            {
                dispatcher.BeginInvoke(() => { Mensaje = "GUARDANDO"; Mostrar = true; });
     
                Datos.guardarDatos();
                dispatcher.BeginInvoke(() => { Mostrar = false; });
                
                Navegacion.Actual.GoBack();
            }
        }

    Todo empieza por el comando ICommand que ejecuta Activar, éste crea el hilo secundario que llama al método Comenzar(), el cual manda un serie de datos mediante WCF y muestra el Canvas con un texto.

    Una vez llega el resultado del servicio WCF, ejecuta el método guardar(), el cual actualiza nuevamente el Canvas, para a continuación guardar varios datos usando ISolatedStorageSettings y volver, finalizando con ocultar el Canvas.

    Todo funciona hasta que comienza a guardar los datos, es decir, el canvas se actualiza y muestra "GUARDANDO" pero los datos se guardan extremadamente lentos, cosa que no pasaba cuando no usaba el Thread (aunque no me actualizaba el canvas, jejeje).

    ¿He de entender que hasta el final se está corriendo todo por el segundo hilo creado y que por ello guarda tan lento? ¿Si es así, cómo puedo para el hilo? ¿Qué estoy haciendo mal?.

    Saludos.

    jueves, 22 de marzo de 2012 16:42
  • Hola!

    Exactamente, al ejecutar la llamada al servicio en el Thread secundario que hemos creado, la respuesta nos llega en ese mismo thread secundario, por lo que Datos.GuardarDatos se está ejecutando en el mismo Thread.

    Yo probaría a meter el Datos.GuardarDatos dentro de un nuevo BeginInvoke, no en el mismo en el que actualizas el mensaje, para que se ejecute en el thread principal, tan como se hacía anteriormente.

    Un saludo!


    MCTS - .NET 4.0 Windows Applications
    MCTS - .NET 4.0 Data Access
    MCTS - .NET 4.0 Service Communication Applications
    MCPD - .NET 4.0 Windows Developer
    Visita mi Blog en Geeks.ms
    Sigueme en Twitter

    viernes, 23 de marzo de 2012 5:02
    Moderador
  • Si sustituyo:

            private void guardar()
            {
                dispatcher.BeginInvoke(() => { Mensaje = "GUARDANDO"; Mostrar = true; });
     
                Datos.guardarDatos();
                dispatcher.BeginInvoke(() => { Mostrar = false; });
     
                Navegacion.Actual.GoBack();
            }

    por:

            private void guardar()
            {
                dispatcher.BeginInvoke(() => { Mensaje = "GUARDANDO"; Mostrar = true; });
     
                dispatcher.BeginInvoke(() => { Datos.guardarDatos(); });
     
                dispatcher.BeginInvoke(() => { Mostrar = false; });
     
                Navegacion.Actual.GoBack();
            }

    guarda rápido pero no actualiza el Canvas a "GUARDANDO", es como si se saltase el primer dispatcher.

    ¿?

    domingo, 25 de marzo de 2012 18:43
  • Hola

    Esto pasa porque el dispatcher hace que el código se ejecute en el hilo de interface de usuario, por lo que no se refresca hasta que no termine. ¿Puedes enseñarnos el código de guardarDatos???

    Aunque guarde un poco más despacio, yo preferiría ejecutar datos.guardardatos() en el hilo secundario (como en tu primer ejemplo) de esta forma tu UI sigue respondiendo y no se bloquea que es lo más importante.

    Un saludo!


    MCTS - .NET 4.0 Windows Applications
    MCTS - .NET 4.0 Data Access
    MCTS - .NET 4.0 Service Communication Applications
    MCPD - .NET 4.0 Windows Developer
    Visita mi Blog en Geeks.ms
    Sigueme en Twitter

    jueves, 29 de marzo de 2012 5:22
    Moderador
  • El código de guardarDatos() sería el siguiente:

            public static void guardarDatos(Tipo tipo, object datos)
            {
                try
                {
                    lock (flag)
                    {
                        var settings = IsolatedStorageSettings.ApplicationSettings;
                        if (settings.Contains(tipo.ToString())) { settings.Remove(tipo.ToString()); }
                        settings.Add(tipo.ToString(), datos);
                        settings.Save();
                    }
                }
                catch (Exception error) { }
            }

    El problema es que la diferencia entre el tiempo de guardado es bastante considerable, más del triple.

    ¿?

    jueves, 29 de marzo de 2012 16:52