2011-05-25 13 views
15

Hace algún tiempo escribí una pequeña aplicación tipo widget que se suponía debía hacer un seguimiento de las tareas, cada tarea tenía una fecha límite especificada como DateTime, ahora si desea mostrar cuánto tiempo queda hasta la fecha límite es posible que desee unirse a una "virtual" (* maldice la palabra clave virtual *) propiedad como esta:Encuadernación a propiedades dependientes del tiempo

public TimeSpan TimeLeft 
{ 
    get { return Deadline - DateTime.Now; } 
} 

Obviamente, en teoría, esta propiedad cambia cada garrapata y que desee actualizar su interfaz de usuario de cada de vez en cuando (p. ej., extrayendo periódicamente un evento PropertyChanged para esa propiedad).

Cuando escribí el widget, actualicé la lista de tareas completa cada minuto, pero esto no es ideal ya que si el usuario interactúa con algún elemento (por ejemplo, escribiendo en un TextBox que se une a un Comments -property) que será ásperamente interrumpido y las actualizaciones de la fuente se pierden.

Entonces, ¿cuál podría ser el mejor enfoque para la actualización de la interfaz de usuario si tiene propiedades dependientes del tiempo como este?

(No consumo que la aplicación más por cierto, sólo pensé que esto era una pregunta muy interesante)

+1

Usted sabe más acerca de estas cosas que yo, así que estoy curioso - fuera del DP del INPC y de qué eventos o acciones hacen que el sistema de unión a volver a evaluar todas las vinculaciones disponible en la interfaz de usuario? ¿Hay alguno? Si es así, ¿se puede confiar en que alguno de ellos se produzca de manera confiable? – Tim

+0

@Tim: No se puede pensar en ninguno, bueno, cuando se modificó el DataContext que causa actualizaciones (para los enlaces que se unen a él) en el subárbol debido a la herencia. En WPF tiene acceso público a la (http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.datacontextchanged.aspx) evento asociado [DataContextChanged' ']. En Silverlight es interno por alguna razón. –

+0

Podría valer la pena establecer como requisito de diseño de WPF que todas las propiedades tengan definidores y que el captador siempre devuelva el valor establecido. De lo contrario, decir funciones como la anterior representa que la GUI es esencialmente una mentira. –

Respuesta

3

Creo que lo que ha dicho en su primer párrafo después de que el ejemplo de código es la única manera razonable para hacer que esto funcione en WPF. Configure un temporizador que solo llame al PropertyChanged para la propiedad TimeLeft. El intervalo puede variar de acuerdo con su situación (si habla de una lista de tareas semanal, probablemente solo necesite actualizarla cada 5 minutos más o menos. Si está hablando de una lista de tareas durante los próximos 30 minutos, es posible que necesite actualizarlo cada minuto o 30 segundos o algo así.

Ese método evitaría los problemas que usted ha mencionado, con la opción de actualización, ya que sólo los TimeLeft fijaciones se vería afectada. Si tuviera millones de estas tareas, supongo que la penalización en el rendimiento sería bastante significativo. Pero si solo tenías unas pocas docenas o algo así, actualizar esos enlaces cada 30 segundos sería un asunto bastante insignificante, ¿no?

Todas las posibilidades que se me ocurren utilizan temporizadores o animaciones. animaciones serían w Puede ser demasiado "pesado" al agregar tareas a la lista. Y de los escenarios del temporizador, el de arriba parece ser el más limpio, el más simple y el más práctico. Probablemente todo se reduce a si funciona o no para su escenario específico.

+2

También pensé que el uso de un temporizador puede ser el mejor enfoque, una mejora podría ser el uso de un solo temporizador estático para todas las tareas y para hacer que el temporizador de desconexión automática si se minimiza la aplicación. –

+0

Sí, definitivamente hay algunas optimizaciones que podrían hacerse. Ambas son buenas sugerencias. – Tim

+1

* accpeted hasta que un mago se detiene por y ofrece algo sorprendentemente superiores: P * –

4

Un temporizador es la única manera que se me ocurre. Dado que esta es una pregunta interesante, voy a poner mi .02 en Me encapsular que hacer algo como esto:.

public class CountdownViewModel : INotifyPropertyChanged 
{ 
    Func<TimeSpan> calc; 
    DispatcherTimer timer; 

    public CountdownViewModel(DateTime deadline) 
     : this(() => deadline - DateTime.Now) 
    { 
    } 

    public CountdownViewModel(Func<TimeSpan> calculator) 
    { 
     calc = calculator; 

     timer = new DispatcherTimer(); 
     timer.Interval = TimeSpan.FromSeconds(1); 
     timer.Tick += timer_Tick; 
     timer.Start(); 
    } 

    void timer_Tick(object sender, EventArgs e) 
    { 
     var temp = PropertyChanged; 
     if (temp != null) 
     { 
      temp(this, new PropertyChangedEventArgs("CurrentValue")); 
     } 
    } 

    public TimeSpan CurrentValue 
    { 
     get 
     { 
      var result = calc(); 
      if (result < TimeSpan.Zero) 
      { 
       return TimeSpan.Zero; 
      } 
      return result; 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

public class MyViewModel 
{ 
    public CountdownViewModel DeadlineCountdown { get; private set; } 

    public DateTime Deadline { get; set; } 

    public MyViewModel() 
    { 
     Deadline = DateTime.Now.AddSeconds(200); 
     DeadlineCountdown = new CountdownViewModel(Deadline); 
    } 
} 

entonces se podría obligar a DeadlineCountdown.CurrentValue directamente, o crear un CountdownView. Puede mover el temporizador al CountdownView, si lo desea. Podría usar un temporizador estático para que todos se actualicen al mismo tiempo.

Editar

Si Deadline va a cambiar, se tendría que construir la cuenta atrás de la siguiente manera:

DeadlineCountdown = new CountdownViewModel(() => this.Deadline - DateTime.Now); 
+1

buen esfuerzo, aunque algunas cosas: Usted no necesita un temporizador despachador para ello, ya no accede a los objetos afines hilo, promover la encapsulación podría tener La desventaja de que el ajuste de la cuenta regresiva se vuelve más complejo cuando se cambia la fecha límite, ya que la propiedad ya no depende directamente de ella como en mi código inicial. Además, la construcción de la clase me parece un poco confusa, pero probablemente podría arreglarse con cierta documentación sobre los constructores. –

+0

Buen punto sobre el temporizador del despachador: estaba pensando en hacer una Vista y hacer una "encuadernación manual", así que supongo que tenía temporizadores de despachador en la mente. No consideré actualizar la fecha límite; esto hace que el primer constructor sea inútil, pero aún podrías usar el segundo. –

+0

Posiblemente hacer que el primero tome Deadline usando 'ref' podría funcionar también, ¿no? –

1

He leído su respuesta aceptada, pero me estaba preguntando ... ¿por qué no solo deshabilitar los enlaces para esa tarea específica mientras está en modo 'Editar' para que no sea interrumpido?¿Entonces simplemente vuelve a habilitar ese enlace cuando termines o cancelas tu edición? De esa manera, incluso si su temporizador se actualiza cada segundo, ¿a quién le importa?

En cuanto a cómo deshabilitarlos sin desconectarlos (y restablecer así su valor), simplemente defina un indicador booleano, luego, en todos los DP que desee interrumpir, busque ese indicador en la lógica de validación. Si el indicador es verdadero y el DependencyObject al que se aplica es el que está editando, bloquee el cambio al DP.

De todos modos, esto sólo vino a la cabeza. No lo he probado, pero debería ser algo fácil de probar.

Cuestiones relacionadas