2010-05-26 61 views
11

我試圖找到一種更清潔的替代方案(即Scala的慣用方法),以便在WPF/silverlight數據綁定中使用數據綁定 - 即實現INotifyPropertyChanged時看到的那種東西。首先,一些背景:慣用屬性更改scala中的通知?

在.net WPF或silverlight應用程序中,您有雙向數據綁定的概念(即將UI的某些元素的值綁定到DataContext的.net屬性中改變UI元素的方式會影響屬性,反之亦然,一種方法是在你的DataContext中實現INotifyPropertyChanged接口,不幸的是,這會爲你添加到「ModelView」的任何屬性引入很多樣板代碼「類型下面是它可能看起來在斯卡拉:

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

出於節省空間的考慮,我們假設INotifyPropertyChanged的類型是管理型的回調(AnyRef,字符串列表的特質)=> Unit,並且OnPropertyChanged是調用所有這些回調的方法,傳遞「this」作爲AnyRef和傳入的String)。這只是C#中的一個事件。

你可以立即看到問題:這是大量的樣板代碼只有兩個屬性。我一直想寫這樣的事情,而不是:

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

我知道,我可以很容易地把它寫這樣的,如果ObservableProperty [T]具有價值/ VALUE_ =方法(這是我使用的方法現在):

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

但是,有沒有辦法使用implicits或斯卡拉的一些其他功能/成語,使ObservableProperty實例功能,好像他們是普通的「屬性」,在斯卡拉實施的第一個版本,而無需調用價值的方法?我能想到的唯一的另一件事是這樣的,這是比任何上述兩個版本的更詳細,但仍比原來少了冗長:

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

本文可能會讓您感興趣:http://www.ganguin.net/frp2d.pdf – 2010-05-26 19:45:30

+0

另請參閱http:// stackoverflow。com/questions/1054179/functional-reactive-programming-in-scala目前爲止沒有成功。不幸的是,這是斯卡拉糟糕的設計決定。 – thSoft 2010-05-27 09:03:52

回答

2

我不能宣稱這是一個Scala的規範性質變化框架,但我以前用過的一類這樣的:

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

創建這個類是,我想一個靈活的線程安全的方式對變化做出反應,此規定的動機(因爲它提供了兩個回調並可以將消息推送給參與者)。

在我看來,除非我誤解了你想要的東西,因爲我沒有機會學習WPF/Silverlight的東西 - 這可以實現你想要的一切和更多。

例如,

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

應大致相當於你想要什麼。 (再說一遍,我不確定你想要的靈活性有多靈活;你可以創建一組符號 - > notifier映射,你可以用apply方法查找,這樣當目標獲得時,目標就會有一個更容易做的事情DrawOrder符號。)

與您的使用唯一明顯區別是通告程序使用其應用程序/更新方法來保存樣板文件;你不必每次都寫def x和def x_ =方法,但是你必須使用()來訪問。