Frage

Ich versuche, eine sauberere Alternative zu finden, um die Art von Sache, die Sie mit zu sehen (die zu Scala idiomatischen ist) Datenbindung in WPF / Silverlight-Datenbindung -, die Umsetzung INotifyPropertyChanged ist. Zunächst einiger Hintergrund:

In .NET WPF oder Silverlight-Anwendungen, haben Sie das Konzept der Zwei-Wege-Datenbindung (das heißt, die Bindung mit dem Wert eines Elements der Benutzeroberfläche zu einer .net-Eigenschaft des Datacontext so dass Änderungen an das UI-Element wirken sich auf die Eigenschaft, und umgekehrt. eine Möglichkeit, dies zu ermöglichen, ist die INotifyPropertyChanged-Schnittstelle in Ihrem Datacontext zu implementieren. Leider dies führt für jede Eigenschaft eine Menge Standardcode Sie zum „Modelview“ Typ. Hier ist, wie es könnte in Scala aussehen:

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
          }
      }
}

Aus Gründen des Raumes, nehmen sie an, der INotifyPropertyChanged-Typ ist ein Merkmal, das eine Liste der Rückrufe vom Typ verwaltet (AnyRef, String) => Einheit, und dass OnPropertyChanged ein Verfahren ist, dass Invokes all diese Rückrufe, vorbei an „this“ als AnyRef und die übergebenen in String). Dies würde nur ein Ereignis in C # sein.

Sie können das Problem sofort sehen: die für nur zwei Eigenschaften eine Tonne Standardcode ist. Ich habe immer zu etwas schreiben wie diese stattdessen wollte:

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
          }
      }
}

Ich weiß, dass ich leicht es so schreiben kann, wenn ObservableProperty [T] hat Wert / VALUE_ = Methoden (dies ist die Methode, die ich jetzt bin mit):

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))
        }
    }
}

Aber ist es eine Möglichkeit, die erste Version mit implicits oder einem anderen Funktion / Idiom der Scala zu implementieren, um ObservableProperty Instanzen funktionieren, als ob sie in scala normale „Eigenschaften“ waren, ohne dass die Wert Methoden nennen? Das einzige, was ich denken kann, ist so etwas wie dieses, das als einen der beiden oben genannten Versionen ausführlicher ist, ist aber immer noch weniger ausführlich als das 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 
    }
  }
}
War es hilfreich?

Lösung

Ich konnte nicht behaupten, dass dies eine kanonische Eigenschaftsänderung Rahmen in Scala, aber ich habe eine Klasse wie folgt vor verwendet:

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) }
}

Die Motivation für die Erstellung dieser Klasse war, dass ich eine flexible Thread-sichere Art und Weise auf Veränderungen reagieren wollte, was diese bieten (wie es beiden Rückrufe liefert und Nachrichten an Akteuren schieben kann).

Es scheint mir - es sei denn, ich bin Missverständnis genau das, was Sie wollen, weil ich nicht gehabt haben Gelegenheit, die WPF / Silverlight Dinge zu lernen -., Dass dies alles umsetzen können Sie wollen und mehr

Beispiel:

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
  } 
}

sollten etwa gleich sein, was Sie wollen. (Noch einmal, ich bin mir nicht sicher, wie flexibel Sie dies sein wollen, Sie eine Reihe von Symbol schaffen könnten -> Notifier-Mappings, dass Sie mit einem Blick würden anwenden Methode so das Ziel eine einfachere Zeit, etwas zu tun haben würde, wenn es bekommt das DrawOrder Symbol).

Der einzige wesentliche Unterschied aus Ihrer Nutzung ist, dass der Notifier verwendet, um seine Anwendung / Aktualisierungsmethoden vorformulierten zu speichern; Sie nicht schreiben müssen def x und def x_ Methoden = jedes Mal, aber Sie verwenden müssen () für den Zugriff.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top