Código de UI no bloqueado en WP8 -- # campo con task-parallel-library campo con windows-phone camp codereview Relacionados El problema

Non locking UI code in WP8


5
vote

problema

Español

Recientemente comencé a profundizar en el mundo de Windows Phone 8 Dev, y como una experiencia de aprendizaje, he decidido hacer una solicitud de cronómetro. Ahora, dado que he sido un tipo web la mayor parte de mi carrera, nunca he encontrado problemas de bloqueo de hilo de UI, por lo que la solución con la que he llegado para garantizar que mi clase de cronómetro esté actualizando el hilo de UI "correctamente". en mi opinión, al menos) en necesidad de una revisión.

Por lo tanto, mis preguntas generales con el siguiente código son;

  1. lo estoy haciendo "¿verdad"? Me doy cuenta de que puede ser un poco amplio; Solo quiero asegurarme de que no estoy cometiendo ningunos pecados cardinales del desarrollo de la UI.

  2. ¿Hay enfoques más simples para lo que he hecho?

  3. ¿Hay alguna mejora en el código que alguien sugiera? No estoy seguro de usar Thread.Sleep es una buena idea, pero cuando lo saco, termina bloqueando nuevamente el hilo de UI. Por eso estoy pidiendo la entrada.

mi clase de cronómetro:

  public class StopwatchClass : IDisposable {     DispatcherTimer _timer;      public int Interval { get; set; }      public int Seconds { get; set; }     public int Minutes { get; set; }     public int Hours { get; set; }      public bool IsRunning { get { return _timer.IsEnabled; } }     public TimeSpan Elapsed { get; set; }     public List<TimeSpan> Splits { get; set; }      public StopwatchClass(int interval)     {         this.Splits = new List<TimeSpan>();          _timer = new DispatcherTimer();         _timer.Interval = new TimeSpan(0, 0, interval);         _timer.Tick += timer_Tick;     }      void timer_Tick(object sender, EventArgs e)     {         this.Seconds += 1;         if (this.Seconds == 60)         {             this.Minutes += 1;             this.Seconds = 0;         }         if (this.Minutes == 60)         {             this.Hours += 1;             this.Minutes = 0;         }     }      public void Start()     {         _timer.Start();     }      public void Stop()     {         _timer.Stop();       }     public void Reset()     {         _timer = new DispatcherTimer();         this.Seconds = 0;         this.Minutes = 0;         this.Hours = 0;         this.Splits.Clear();     }      public void Split()     {         var timeSpan = new TimeSpan(this.Hours, this.Minutes, this.Seconds);         this.Splits.Add(timeSpan);     }      public void Dispose()     {         _timer.Tick -= timer_Tick;     } }   

y el código de botón WP8 (SecondValue es un bloque de texto en la página WP8).

  my $re_filter = qr/some special text/; my $reject_filter = sub {         my $v = shift;         return 1 if ref $v eq 'SCALAR' && $v =~ $re_filter;     };  my $p = Mojo::Promise::Filter->new(         reject_filter => $reject_filter,         promises => @promises     );  return $p->filter; 0  
Original en ingles

I've recently started to delve into the world of windows phone 8 dev, and as a learning experience I've decided to make a stopwatch application. Now, given I've been a web guy most of my career, I've never come across UI thread locking issues, so the solution I've come up with to ensure my stopwatch class is updating the UI thread 'correctly' is (in my opinion at least) in need of a review.

So my overall questions with the below code are;

  1. Am I doing it "right"? I realise that can be kind of broad; I just want to make sure I'm not committing any cardinal sins of UI development.

  2. Are there simpler approaches to what I've done?

  3. Are there any improvements to the code that someone would suggest? I'm not sure using thread.sleep is a good idea, but when I take it out, it ends up locking the UI thread again. Hence why I'm asking for input.

My StopWatch Class:

public class StopwatchClass : IDisposable {     DispatcherTimer _timer;      public int Interval { get; set; }      public int Seconds { get; set; }     public int Minutes { get; set; }     public int Hours { get; set; }      public bool IsRunning { get { return _timer.IsEnabled; } }     public TimeSpan Elapsed { get; set; }     public List<TimeSpan> Splits { get; set; }      public StopwatchClass(int interval)     {         this.Splits = new List<TimeSpan>();          _timer = new DispatcherTimer();         _timer.Interval = new TimeSpan(0, 0, interval);         _timer.Tick += timer_Tick;     }      void timer_Tick(object sender, EventArgs e)     {         this.Seconds += 1;         if (this.Seconds == 60)         {             this.Minutes += 1;             this.Seconds = 0;         }         if (this.Minutes == 60)         {             this.Hours += 1;             this.Minutes = 0;         }     }      public void Start()     {         _timer.Start();     }      public void Stop()     {         _timer.Stop();       }     public void Reset()     {         _timer = new DispatcherTimer();         this.Seconds = 0;         this.Minutes = 0;         this.Hours = 0;         this.Splits.Clear();     }      public void Split()     {         var timeSpan = new TimeSpan(this.Hours, this.Minutes, this.Seconds);         this.Splits.Add(timeSpan);     }      public void Dispose()     {         _timer.Tick -= timer_Tick;     } } 

And the WP8 button code (secondsValue is a text block on the wp8 page).

private async void startStopwatch_Click(object sender, RoutedEventArgs e)     {         watch.Start();           await Task.Factory.StartNew(() =>         {             while (watch.IsRunning)             {                 Thread.Sleep(1000);                 Dispatcher.BeginInvoke(() => secondsValue.Text = watch.Seconds.ToString());             }          });     } 
        

Lista de respuestas

3
 
vote
vote
La mejor respuesta
 

menor

  1. Debe deshacerse del sufijo 9988776665544330 . No hay ningún valor para adjuntar el tipo como un sufijo al nombre.

  2. No es necesario que escriba this en todas partes para acceder a los miembros.

  3. Tiene una propiedad 9988776655544332 que no se usa.

bugs / mayor

  1. Su método Reset() romperá el cronómetro porque creará un nuevo temporizador, pero no vuelva a conectar el controlador de eventos del temporizador.

  2. Usted tiene un método 99887766655443344 que configura el temporizador, pero también hace esto en el constructor, que es una duplicación de código y, de hecho, ha introducido un error (consulte el punto anterior). Seco - No te repitas. Hacer uso de un método común que pueda llamarse en ambos lugares.

  3. En lugar de "sondear" su cronómetro en un bucle continuo, su StopwatchClass debe exponer un evento como TimeUpdated a la que se puede conectar una parte interesada. < / p>

    En general, esto puede plantear problemas cuando interactúan con la interfaz de usuario porque si la devolución de llamada ocurre en un hilo diferente, entonces necesita el acceso a la interfaz de usuario de Marshal a través del hilo de UI. Sin embargo, ha utilizado un 9988776665544337 que se evalúa en la parte superior de cada bucle de despachador, lo que significa que las devoluciones de llamada están sucediendo en el hilo de la interfaz de usuario y es seguro acceder a los elementos de UI desde la devolución de llamada (consulte el ejemplo de msdn ).

    Así que, básicamente, puede exponer un evento que se disparará en la devolución de llamada del temporizador y cualquier código que se enganche para que pueda actualizar la UI.

  4. En general MVVM se ha establecido como un patrón muy útil. Lo que significa que, en lugar de actualizar los elementos de la UI directamente, actualizaría un modelo de vista observable a la que está sujeta la UI. Este desacoplio, la UI, mejor de los datos y el desacoplamiento, siempre es bueno.

  5. Se garantiza que el temporizador no se dispare antes de que haya transcurrido el intervalo, pero no hay garantía sobre cuánto tiempo ha transcurrido el intervalo, se incendiará. Esto significa que su cronómetro comenzará a caer detrás de la época real, ya que asume que cada marca es de 1 segundo, mientras que, de hecho, puede ser de 1.01 segundos. Esto significa después de 100 segundos, podría ser fácilmente de 1 seg. Una rebanada de tiempo de rosca en Windows suele ser de alrededor de 10 ms, así que sospeche que el temporizador esté en torno a esa precisión. Aunque en Windows Phone podría obtener un mejor rendimiento (no se pudo encontrar ninguna documentación en ese recurso).

    La forma de superar esto es para tomar una marca de tiempo en Start y calcular la diferencia a la hora actual en cada marca. Si bien esto probablemente también te dará un aprox. 10ms Precisión Será constante mientras su solución acumulará un error en cada garrapata.

    Esto también aliviará la necesidad de calcular los segundos, los minutos y las horas desde que pasan, ya que puede obtener eso del Timespan .

Con los cambios desde arriba, su código podría verse así:

  this0  
 

Minor

  1. You should get rid of the Class suffix. There is no value in attaching the type as a suffix to the name.

  2. You don't need to write this everywhere to access members.

  3. You have an Elapsed property which is not used.

Bugs/Major

  1. Your Reset() method will break the stopwatch because you create a new timer but you do not re-attach the timer tick event handler.

  2. You have a Reset() method which sets up the timer but you also do this in the constructor which is some code duplication and in fact has introduced a bug (see previous point). DRY - Don't Repeat Yourself. Make use of a common method which can get called in both places.

  3. Instead of "polling" your stopwatch in a continuous loop your StopwatchClass should expose an event like TimeUpdated to which any interested party can hook up to.

    In general this can pose problems when interacting with UI because if the callback happens on a different thread then you need to marshal UI access across to the UI thread. However you have used a DispatcherTimer which is evaluated at the top of every dispatcher loop - which means the callbacks are happening on the UI thread and it is safe to access UI elements from within the callback (see the MSDN example).

    So basically you can expose an event which gets fired on the timer callback and whatever code hooks up to it can update the UI.

  4. In general MVVM has established itself as a very useful pattern. Which means that rather than updating UI elements directly you would update an observable view model to which the UI is bound. This decouples the UI better from the data and decoupling is always good.

  5. The timer is guaranteed to not fire before the interval has elapsed - but there is no guarantee about how long after the interval has elapsed it will fire. This means your stopwatch will start falling behind the real time as you assume each tick is 1 second while in fact it might be 1.01 sec or so. This means after 100 seconds you could be easily 1 sec off or more. A thread time slice in windows is typically around 10ms so I'd suspect the timer to be around that accuracy. Although on Windows Phone you might get better performance (couldn't find any documentation in that regards).

    The way to overcome this is to take a timestamp at Start and compute the difference to the current time on each tick. While this will probably also give you an approx. 10ms accuracy it will be constant while your solution will accumulate an error on each tick.

    This will also alleviate the need to compute the seconds, minutes and hours since passing as you can get that from the Timespan.

With the changes from above your code could look like this:

public class StopwatchClass : IDisposable {     DispatcherTimer _timer;      public int Interval { get; set; }      public int Seconds { get; set; }     public int Minutes { get; set; }     public int Hours { get; set; }      public bool IsRunning { get { return _timer.IsEnabled; } }     public TimeSpan Elapsed { get; set; }     public List<TimeSpan> Splits { get; set; }      private DateTime _StartTimestamp = DateTime.MaxValue;      public event EventHandler TimeUpdated;     private void OnTimeUpdated()     {         var handler = TimeUpdated;         if (handler != null)         {             TimeUpdated(this, EventArgs.Empty);         }     }      public StopwatchClass(int interval)     {         Reset();     }      void timer_Tick(object sender, EventArgs e)     {         var timeSinceStart = DateTime.Now - _StartTimestamp;         Seconds = (int)timeSinceStart.Seconds;         Minutes = (int).timeSinceStart.Minutes;         Hours = (int)timeSinceStart.TotalHours;         OnTimeUpdated();     }      public void Start()     {         _StartTimestamp = DateTime.Now;         _timer.Start();     }      public void Stop()     {         _StartTimestamp = DateTime.MaxValue         _timer.Stop();     }      public void Reset()     {         Seconds = 0;         Minutes = 0;         Hours = 0;         Splits = new List<TimeSpan>();         _timer = new DispatcherTimer();         _timer.Interval = new TimeSpan(0, 0, interval);         _timer.Tick += timer_Tick;     }      public void Split()     {         var timeSpan = new TimeSpan(Hours, Minutes, Seconds);         Splits.Add(timeSpan);     }      public void Dispose()     {         _timer.Tick -= timer_Tick;     } } 
 
 
   
   

Relacionados problema

1  Almacenamiento de datos de DataService  ( Dataservice data storage ) 
Tengo un 9988776655544330 , que debe hablar con el servidor y almacenar vars (para llamadas complicadas, por ejemplo). La última vez, terminé con este enfo...

3  Uso de la lista de tareas para obtener y almacenar datos de caché  ( Using list of tasks to obtain and cache data ) 
Desarrollo la aplicación Windows Phone con Prism Framework (MVVM). Yo uso el almacenamiento en caché de datos. Para obtener datos, utilizo el servicio pro...

8  Uso de palabras clave ASYNC / AWAIT en las consultas de la base de datos (Windows Phone 8)  ( Using keywords async await in database queries windows phone 8 ) 
Tengo una base de datos local en la aplicación Windows Phone 8 . La aplicación incluye muchas consultas a la base de datos y no quiero mal efecto sobre la ca...

3  Logger WP7 más simple  ( Simplest wp7 logger ) 
Acabo de terminar con este enfoque para WP7 (sin etiqueta todavía), en caso de que alguien lo encuentre útil. Además, las consideraciones de mejora son bienve...

6  MEJORAS DE MARCKUP XAML  ( Xaml markup improvements ) 
Tengo una aplicación de Windows Store y Windows Phone, y en esta aplicación tengo muchas páginas como esta: <Grid Background="Transparent"> <ScrollView...

5  Comprobando nuevas actualizaciones de software al cliente  ( Checking for new software updates to the client ) 
He escrito un servicio web muy simple con MVC y WebAPI. Ahora estoy trabajando en el código del cliente, que será una aplicación WPF (y pronto la aplicación W...

1  Islosettingsmanager  ( Isosettingsmanager ) 
Solo una envoltura alrededor de isolatedstoragesettings. Lo único de lo que no estoy seguro de que está ISOSETTINGS.SAVE (); ¿Debo llamarlo allí? Pareciendo...

4  Llamadas asíncronas API con delegados y manejadores de eventos  ( Asynchronous api calls with delegates and event handlers ) 
Estoy haciendo una aplicación de WindowsPhone 8 en C #. Con esto una API, así que puedo convertir los datos de JSON en objetos útiles. He encontrado un método...

8  Usando las descripciones de Enum a la encuadernación de cadena / texto  ( Using enum descriptions to string text binding ) 
En mi aplicación WP8, necesito mostrar un par de botones de radio para que el usuario seleccione un valor de un enum . No quiero concojo hardco, ya sea el va...

4  Isostoraganager  ( Isostoragemanager ) 
un gerente para un rápido ahorro de objetos ASYNC a almacenamiento aislado, utilizando la serialización de NewTonsoft.Json. Un proyecto para jugar es aquí . ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos