Skip to content

Instantly share code, notes, and snippets.

@peace2048
Created October 24, 2013 11:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peace2048/7135362 to your computer and use it in GitHub Desktop.
Save peace2048/7135362 to your computer and use it in GitHub Desktop.
PropertyChanged イベントを IObservable で監視しやすいように、INotifyPropertyChanged を拡張します。
Imports System
Imports System.ComponentModel
Imports System.Linq
Imports System.Linq.Expressions
Imports System.Reactive.Linq
Imports System.Runtime.CompilerServices
''' <summary>
''' INotifyPropertyChanged に拡張メソッドを追加し、PropertyChanged イベントを IObservable で監視可能にします。
''' </summary>
Public Module PropertyChangedExtensions
''' <summary>
''' PropertyChanged イベントを IObservable に変換します。
''' </summary>
''' <typeparam name="TSender">監視対象の型</typeparam>
''' <param name="sender">監視対象</param>
''' <returns>
''' 基本的に、Reactive の Observable.FromEventPattern と同じですが、
''' Rective の EventPattern は、Sender が Object として格納されていますので、
''' 元の型で格納される PropertyChangedEventPattern で通知します。
'''
''' Property 拡張メソッドと共に使用されることを想定しています。
''' </returns>
''' <remarks>
''' 別スレッドで通信を行い、サーバとの通信状態を IsOnline プロパティに設定すると同時に PropertyChanged で通知するとする。
''' 次のように、記述すれば ObserveOn で MainWindow の UIスレッドで Subscribe できる。
''' <example>
''' Dim obj As INotifyPropertyChanged
''' obj.AsObservablePropertyChanged().Property(Function(o) o.IsOnline).ObserveOn(MainWindow).Subscribe(Sub(value) StatusLabel.Text = If(value, "オンライン", "オフライン"))
''' </example>
''' 複数のプロパティを監視する場合は、次のようにも記述できる。
''' <example>
''' Dim obj As INotifyPropertyChanged
''' Dim propChange = obj.AsObservablePropertyChanged().ObserveOn(MainWindow)
''' propChange.Property(Function(o) o.IsOnline).Subscribe(Sub(value) StatusLabel.Text = If(value, "オンライン", "オフライン"))
''' propChange.Property(Function(o) o.CurrentWork).Subscribe(
''' Sub(work)
''' If work Is Nothing Then
''' WorkNameLabel.Text = ""
''' Else
''' WorkNameLabel.Text = work.Name
''' End If
''' End Sub)
''' </example>
''' </remarks>
<Extension>
Public Function AsObservablePropertyChanged(Of TSender As INotifyPropertyChanged)(sender As TSender) As IObservable(Of PropertyChangedEventPattern(Of TSender))
Return From eventPattern In Observable.FromEventPattern(Of PropertyChangedEventArgs)(sender, "PropertyChanged")
Select New PropertyChangedEventPattern(Of TSender)(
sender:=DirectCast(eventPattern.Sender, TSender),
propertyName:=eventPattern.EventArgs.PropertyName)
End Function
''' <summary>
''' PropertyChangedEventPattern の監視可能なシーケンスから、指定したプロパティを抜き出して、プロパティの値を監視可能なシーケンスとして返します。
''' </summary>
''' <typeparam name="TSender">プロパティを取得するオブジェクトの型</typeparam>
''' <typeparam name="TValue">プロパティの値の型</typeparam>
''' <param name="source">PropertyChangedEventPattern の監視可能なシーケンス</param>
''' <param name="propertyExpression">プロパティの値を返す式</param>
''' <returns>プロパティ値の監視可能なシーケンス</returns>
''' <remarks></remarks>
<Extension>
Public Function [Property](Of TSender As INotifyPropertyChanged, TValue)(source As IObservable(Of PropertyChangedEventPattern(Of TSender)), propertyExpression As Expression(Of Func(Of TSender, TValue))) As IObservable(Of TValue)
Dim memberExpression = DirectCast(propertyExpression.Body, MemberExpression)
Dim propertyName = memberExpression.Member.Name
Dim getter = propertyExpression.Compile()
Return From a In source
Where a.PropertyName = propertyName
Select getter(a.Sender)
End Function
End Module
''' <summary>
''' オブジェクトとプロパティ名を保持するクラス。
'''
''' INotifyPropertyChanged の拡張メソッドである AsObservablePropertyChanged で使用されます。
''' </summary>
''' <typeparam name="TSender">The type of the sender.</typeparam>
Public Class PropertyChangedEventPattern(Of TSender As INotifyPropertyChanged)
''' <summary>
''' Initializes a new instance of the <see cref="PropertyChangedEventPattern(Of TSender)"/> class.
''' </summary>
''' <param name="pattern">The pattern.</param>
Public Sub New(pattern As System.Reactive.EventPattern(Of PropertyChangedEventArgs))
Me.Sender = DirectCast(pattern.Sender, TSender)
Me.PropertyName = pattern.EventArgs.PropertyName
End Sub
''' <summary>
''' Initializes a new instance of the <see cref="PropertyChangedEventPattern(Of TSender)"/> class.
''' </summary>
''' <param name="sender">The sender.</param>
''' <param name="propertyName">Name of the property.</param>
Public Sub New(sender As TSender, propertyName As String)
Me.Sender = sender
Me.PropertyName = propertyName
End Sub
Private _Sender As TSender
''' <summary>
''' Gets the sender.
''' </summary>
''' <value>
''' The sender.
''' </value>
Public Property Sender() As TSender
Get
Return _Sender
End Get
Private Set(ByVal value As TSender)
_Sender = value
End Set
End Property
Private _PropertyName As String
''' <summary>
''' Gets the name of the property.
''' </summary>
''' <value>
''' The name of the property.
''' </value>
Public Property PropertyName() As String
Get
Return _PropertyName
End Get
Private Set(ByVal value As String)
_PropertyName = value
End Set
End Property
End Class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment