Jer*_*ell 11 scala scala-2.8 scala-2.7
我试图在WPF/silverlight数据绑定中使用数据绑定看到一种更清晰的替代方案(对于Scala来说是惯用的) - 即实现INotifyPropertyChanged.首先,一些背景:
在.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
}
}
}
Run Code Online (Sandbox Code Playgroud)
为了空间,让我们假设INotifyPropertyChanged类型是一个管理类型(AnyRef,String)=> 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
}
}
}
Run Code Online (Sandbox Code Playgroud)
我知道如果ObservableProperty [T]具有Value/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))
}
}
}
Run Code Online (Sandbox Code Playgroud)
但有没有办法使用隐含或Scala的其他一些特性/习惯用法来实现第一个版本,以使ObservableProperty实例像scala中的常规"属性"一样运行,而无需调用Value方法?我能想到的唯一的另一件事是这样的,这是比任何上述两个版本的更详细,但仍比原来少了冗长:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
我不能声称这是 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) }
}
Run Code Online (Sandbox Code Playgroud)
创建此类的动机是我想要一种灵活的线程安全方式来响应更改,这提供了(因为它既提供回调又可以将消息推送给参与者)。
在我看来——除非我完全误解了你想要的东西,因为我还没有机会学习 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
}
}
Run Code Online (Sandbox Code Playgroud)
应该大致相当于你想要的。(同样,我不确定您希望它有多灵活;您可以创建一组符号 -> 通知器映射,您可以使用 apply 方法查找它们,这样目标在获得信息时可以更轻松地执行某些操作DrawOrder 符号。)
与您的用法唯一显着的区别是通知程序使用其应用/更新方法来保存样板文件;您不必每次都编写 def x 和 def x_= 方法,但必须使用 () 进行访问。