私は、WPF/silverlight データ バインディングのデータ バインディングで見られるようなもの、つまり INotifyPropertyChanged の実装に代わる、よりクリーンな代替手段 (Scala の慣用句) を見つけようとしています。まず、いくつかの背景:
.Net WPF または Silverlight アプリケーションでは、双方向のデータ バインディング (つまり、UI 要素に変化するような方法で、UI のある要素の値を DataContext の .net プロパティにバインドする) の概念があります。これを有効にする方法の 1 つは、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 がこれらすべてのコールバックを呼び出して AnyRef として「this」を渡すメソッドであると仮定します。 、および渡された文字列)。これは C# の単なるイベントです。
問題はすぐにわかります。たった 2 つのプロパティに対して大量のボイラープレート コードです。私はいつも代わりにこのようなものを書きたいと思っていました:
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))
}
}
}
しかし、Value メソッドを呼び出す必要なく、ObservableProperty インスタンスを scala の通常の「プロパティ」であるかのように機能させるために、暗黙的または Scala の他の機能/イディオムを使用して最初のバージョンを実装する方法はありますか? 私が考えることができる他の唯一のものは、上記の2つのバージョンのどちらよりも冗長ですが、元のものよりも冗長ではありません:
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
}
}
}