我正在尝试找到一种更简洁的替代方案(对于 Scala 来说是惯用的),而不是您在 WPF/silverlight 数据绑定中看到的那种数据绑定 - 即实现 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
}
}
}
为了空间的缘故,我们假设 INotifyPropertyChanged 类型是一个特征,它管理类型为 (AnyRef, String) => Unit 的回调列表,并且 OnPropertyChanged 是一个调用所有这些回调的方法,将“this”作为 AnyRef 传递,以及传入的字符串)。这只是 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/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))
}
}
}
但是有没有办法使用 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
}
}
}