Я пытаюсь найти более чистую альтернативу (идиоматическую для Scala) тому, что вы видите с привязкой данных в привязке данных WPF / silverlight - то есть реализацию INotifyPropertyChanged.Во-первых, немного предыстории:
В приложениях .Net WPF или silverlight у вас есть концепция двусторонней привязки данных (то есть привязка значения некоторого элемента пользовательского интерфейса к свойству .net объектаDataContext таким образом, что изменения в элементе пользовательского интерфейса влияют на свойство, и наоборот. Один из способов включить это - реализовать интерфейс INotifyPropertyChanged в вашем DataContext. К сожалению, это вводит много стандартного кода для любого свойства, которое вы добавляете вТип «ModelView». Вот как это может выглядеть в 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
}
}
}
Ради пространства, давайте предположим, что тип 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
}
}
}
Я знаю, что могу легко написать это так, если ObservableProperty [T] имеет методы Value / Value_ = (это метод I 'м, используя сейчас):
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
}
}
}