Pregunta

Estoy tratando de encontrar una alternativa más limpia (que es idiomático a Scala) para el tipo de cosa que se ve con enlace de datos en WPF / Silverlight enlace de datos - es decir, la aplicación de INotifyPropertyChanged. En primer lugar, algunos antecedentes:

En .Net WPF o aplicaciones Silverlight, que tienen el concepto de dos vías enlace de datos (es decir, el valor vinculante de algún elemento de la interfaz de usuario a una propiedad .NET de la DataContext de tal manera que los cambios de el elemento de la IU afecta a la propiedad, y viceversa. una manera de activar esta es la implementación de la interfaz INotifyPropertyChanged en su DataContext. por desgracia, esta introduce una gran cantidad de código repetitivo para cualquier propiedad que se agrega a la clase "modelview". Aquí es cómo puede ser que mire en Scala:

trait IDrawable extends INotifyPropertyChanged
{    
      protected var drawOrder : Int = 0
      def DrawOrder : Int = drawOrder
      def DrawOrder_=(value : Int) {
            if(drawOrder != value) {
                  drawOrder = value
                  OnPropertyChanged("DrawOrder")
            }
      }

      protected var visible : Boolean = true
      def Visible : Boolean = visible
      def Visible_=(value: Boolean) = {
            if(visible != value) {
                  visible = value
                  OnPropertyChanged("Visible")
            }
      }
      def Mutate() : Unit = {
          if(Visible) {
              DrawOrder += 1 // Should trigger the PropertyChanged "Event" of INotifyPropertyChanged trait
          }
      }
}

Por razones de espacio, asumamos que el tipo INotifyPropertyChanged es un rasgo que gestiona una lista de las devoluciones de llamada de tipo (AnyRef, String) => Unidad, y que OnPropertyChanged es un método que llama a todas aquellas devoluciones de llamada, pasando "este" como el AnyRef, y el pasado-en cadena). Esto no sería más que un evento en C #.

Se puede ver inmediatamente el problema: que es una tonelada de código repetitivo por sólo dos propiedades. Siempre he querido escribir algo como esto en su lugar:

trait IDrawable
{
      val Visible = new ObservableProperty[Boolean]('Visible, true)
      val DrawOrder = new ObservableProperty[Int]('DrawOrder, 0)
      def Mutate() : Unit = {
          if(Visible) {
              DrawOrder += 1 // Should trigger the PropertyChanged "Event" of ObservableProperty class
          }
      }
}

Yo sé que puedo escribir fácilmente de esta manera, si ObservableProperty [T] tiene un valor / = VALUE_ métodos (este es el método que estoy usando ahora):

trait IDrawable {
      // on a side note, is there some way to get a Symbol representing the Visible field
      // on the following line, instead of hard-coding it in the ObservableProperty 
      // constructor?
      val Visible = new ObservableProperty[Boolean]('Visible, true)
      val DrawOrder = new ObservableProperty[Int]('DrawOrder, 0)
      def Mutate() : Unit = {
          if(Visible.Value) {
              DrawOrder.Value += 1 
          }
      }
}

// given this implementation of ObservableProperty[T] in my library
// note: IEvent, Event, and EventArgs are classes in my library for
// handling lists of callbacks - they work similarly to events in C#
class PropertyChangedEventArgs(val PropertyName: Symbol) extends EventArgs("")
class ObservableProperty[T](val PropertyName: Symbol, private var value: T) {
    protected val propertyChanged = new Event[PropertyChangedEventArgs]
    def PropertyChanged: IEvent[PropertyChangedEventArgs] = propertyChanged
    def Value = value;
    def Value_=(value: T) {
        if(this.value != value) {
            this.value = value
            propertyChanged(this, new PropertyChangedEventArgs(PropertyName))
        }
    }
}

Pero ¿hay alguna manera de poner en práctica la primera versión utilizando implícitos o alguna otra característica / idioma de la Scala de hacer ObservableProperty casos funcionan como si fueran regulares "propiedades" en la Scala, sin necesidad de llamar a los métodos de valor? La única otra cosa que puedo pensar es algo como esto, que es más detallado que cualquiera de los anteriores dos versiones, pero es mucho menos detallado que el original:

trait IDrawable {     
  private val visible = new ObservableProperty[Boolean]('Visible, false)
  def Visible = visible.Value
  def Visible_=(value: Boolean): Unit = { visible.Value = value }

  private val drawOrder = new ObservableProperty[Int]('DrawOrder, 0)
  def DrawOrder = drawOrder.Value
  def DrawOrder_=(value: Int): Unit = { drawOrder.Value = value }

  def Mutate() : Unit = {
    if(Visible) {
      DrawOrder += 1 
    }
  }
}
¿Fue útil?

Solución

No podría reclamar que se trata de un marco de cambio de propiedad canónica en Scala, pero yo he utilizado una clase como esto antes:

abstract class Notifier[T,U](t0: T) {
  import java.util.concurrent.atomic.AtomicReference
  import scala.actors.OutputChannel
  type OCUT = OutputChannel[(U,AtomicReference[T])]
  val data = new AtomicReference[T](t0)
  def id: U
  protected var callbacks = Nil:List[T => Unit]
  protected var listeners = Nil:List[OCUT]
  def apply() = data.get
  def update(t: T) {
    val told = data.getAndSet(t)
    if (t != told) {
      callbacks.foreach(_(t))
      listeners.foreach(_ ! (id,data))
    }
  }
  def attend(f: T=>Unit) { callbacks ::= f }
  def attend(oc: OCUT) { listeners ::= oc }
  def ignore(f: T=>Unit) { callbacks = callbacks.filter(_ != f) }
  def ignore(oc: OCUT) { listeners = listeners.filter(_ != oc) }
}

La motivación para la creación de esta clase era que quería una manera thread-safe flexible para reaccionar a los cambios, que esto proporciona (ya que ofrece ambas devoluciones de llamada y puede empujar mensajes a los agentes).

Me parece - a menos que esté malentendido exactamente lo que quieren porque no he tenido ocasión de aprender las cosas WPF / Silverlight -. Que esto puede poner en práctica todo lo que quieras y más

Por ejemplo,

class IDrawable extends SomethingWithOnPropertyChanged {
  val drawOrder = new Notifier[Int,Symbol](0) { def id = 'DrawOrder }
  val visible = new Notifier[Boolean,Symbol](false) { def id = 'Visible }
  drawOrder.attend((i:Int) => OnPropertyChanged(drawOrder.id))
  def mutate {
    if (visible()) drawOrder() += 1
  } 
}

debe ser más o menos equivalente a lo que desea. (Una vez más, no estoy seguro de lo flexible que desea que esto sea; se podría crear un conjunto de símbolo -> asignaciones notificador que se vería con un método de aplicación por lo que el objetivo tendría un tiempo más fácil de hacer algo cuando se pone el símbolo ORDENAOBJETOS.)

La única diferencia significativa de su uso es que el notificador utiliza sus métodos de aplicación / Actualizar para guardar la plancha de caldera; usted no tiene que escribir x def y def = x_ métodos cada vez, pero usted tiene que utilizar () para el acceso.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top