Skip to content

Instantly share code, notes, and snippets.

@peace2048
Last active December 29, 2015 17:19
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/7702985 to your computer and use it in GitHub Desktop.
Save peace2048/7702985 to your computer and use it in GitHub Desktop.
ある `INotifyPropertyChanged` なオブジェクトのプロパティが変化したら 別のオブジェクトのプロパティも更新する
Enum PropertyChainMode
CopyOnly
RaiseOnly
CopyAndRaise
End Enum
Class PropertyChain(Of TSource As {Class, INotifyPropertyChanged}, TTarget)
Private WithEvents _source As TSource
Private _target As TTarget
Private _map As Dictionary(Of String, PropertyMap)
Public Sub New(source As TSource, target As TTarget)
_source = source
_target = target
_map = New Dictionary(Of String, PropertyMap)()
End Sub
Public Sub Add(Of TValue)(
expr As System.Linq.Expressions.Expression(Of Func(Of TValue)),
Optional mode As PropertyChainMode = PropertyChainMode.CopyOnly)
Dim fromExpr = TryCast(expr.Body, System.Linq.Expressions.MemberExpression)
Dim fromPropertyName = fromExpr.Member.Name
Dim getter = expr.Compile()
CreateMap(getter, fromPropertyName, fromPropertyName, mode)
End Sub
Public Sub Add(Of TValue)(
fromExpr As Expression(Of Func(Of TValue)),
toExpr As Expression(Of Func(Of TValue)),
Optional mode As PropertyChainMode = PropertyChainMode.CopyOnly)
Dim fromMemberExpr = TryCast(fromExpr.Body, System.Linq.Expressions.MemberExpression)
Dim fromPropertyName = fromMemberExpr.Member.Name
Dim getter = fromExpr.Compile()
Dim toMemberExpr = DirectCast(toExpr.Body, MemberExpression)
Dim toPropertyName = toMemberExpr.Member.Name
CreateMap(getter, fromPropertyName, toPropertyName, mode)
End Sub
Private Sub CreateMap(Of TValue)(
getter As Func(Of TValue),
fromPropertyName As String,
toPropertyName As String,
mode As PropertyChainMode)
Dim targetType = _target.GetType()
Dim pInfo = targetType.GetProperty(toPropertyName)
Dim eInfo = targetType.GetEvent("PropertyChanged")
Dim fInfo = targetType.GetFields(BindingFlags.Instance Or BindingFlags.NonPublic)
.Where(Function(f) f.FieldType = GetType(PropertyChangedEventHandler))
.FirstOrDefault()
Dim eArgs = New PropertyChangedEventArgs(toPropertyName)
If mode = PropertyChainMode.CopyAndRaise Or mode = PropertyChainMode.RaiseOnly Then
If Not TypeOf _target Is INotifyPropertyChanged Then
Throw New ArgumentException("ターゲットが INotifyPropertyChanged を実装していないので Raise できません", "mode")
End If
End If
Dim copy =
Sub()
Dim v = getter()
pInfo.SetValue(_target, v)
End Sub
Dim raise =
Sub()
Dim d = DirectCast(fInfo.GetValue(_target), MulticastDelegate)
If d IsNot Nothing Then
For Each h In d.GetInvocationList()
h.Method.Invoke(h.Target, New Object() {_target, eArgs})
Next
End If
End Sub
Dim map = New PropertyMap With {.Mode = mode, .Copy = copy, .Raise = raise}
_map.Add(fromPropertyName, map)
End Sub
Private Sub Source_PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Handles _source.PropertyChanged
Dim map As PropertyMap = Nothing
If _map.TryGetValue(e.PropertyName, map) Then
If map.Mode = PropertyChainMode.CopyAndRaise Or map.Mode = PropertyChainMode.CopyOnly Then
map.Copy.Invoke()
End If
If map.Mode = PropertyChainMode.CopyAndRaise Or map.Mode = PropertyChainMode.RaiseOnly Then
map.Raise.Invoke()
End If
End If
End Sub
Public Class PropertyMap
Property Mode As PropertyChainMode
Property Copy As Action
Property Raise As Action
End Class
End Class
Class SampleClass1
Implements INotifyPropertyChanged
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Private _Id As Integer
Public Property Id() As Integer
Get
Return _Id
End Get
Set(ByVal value As Integer)
If _Id <> value Then
_Id = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Id"))
End If
End Set
End Property
Private _Name As String
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
If _Name <> value Then
_Name = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Name"))
End If
End Set
End Property
Public Overrides Function ToString() As String
Return String.Format("SampleClass1{{Id={0}, Name={1}}}", Id, Name)
End Function
End Class
Class SampleClass2
Private _base As SampleClass1
Public Sub New(base As SampleClass1)
_base = base
Dim map = New PropertyChain(Of SampleClass1, SampleClass2)(_base, Me)
map.Add(Function() _base.Id, Function() Me.UserId, PropertyChainMode.CopyOnly)
map.Add(Function() _base.Name, Function() Me.UserName, PropertyChainMode.CopyOnly)
End Sub
Public Property UserId As Integer
Public Property UserName As String
Public Overrides Function ToString() As String
Return String.Format("SampleClass2{{UserId={0}, UserName={1}}}", UserId, UserName)
End Function
End Class
Class SampleClass3
Implements INotifyPropertyChanged
Private _base As SampleClass1
Public Sub New(base As SampleClass1)
_base = base
Dim map = New PropertyChain(Of SampleClass1, SampleClass3)(_base, Me)
map.Add(Function() _base.Id, PropertyChainMode.CopyAndRaise)
map.Add(Function() _base.Name, PropertyChainMode.RaiseOnly)
End Sub
Public Property Id As Integer
Public ReadOnly Property Name As String
Get
Return String.Format("{0}({1})", _base.Name, Id)
End Get
End Property
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Public Overrides Function ToString() As String
Return String.Format("SampleClass3{{Id={0}, Name={1}}}", Id, Name)
End Function
End Class
Module Module1
Sub Main()
Dim c1 = New SampleClass1()
Dim c2 = New SampleClass2(c1)
Dim c3 = New SampleClass3(c1)
AddHandler c1.PropertyChanged, Sub(o, e) Console.WriteLine("c1 property [{0}] changed.", e.PropertyName)
AddHandler c3.PropertyChanged, Sub(o, e) Console.WriteLine("c3 property [{0}] changed.", e.PropertyName)
Console.WriteLine("--- default values ---")
Console.WriteLine(c1)
Console.WriteLine(c2)
Console.WriteLine(c3)
Console.WriteLine("--- c1.Name = AAA ---")
c1.Name = "AAA"
Console.WriteLine("--- values ---")
Console.WriteLine(c1)
Console.WriteLine(c2)
Console.WriteLine(c3)
Console.WriteLine("--- c1.Id = 10 ---")
c1.Id = 10
Console.WriteLine("--- values ---")
Console.WriteLine(c1)
Console.WriteLine(c2)
Console.WriteLine(c3)
Console.ReadLine()
End Sub
End Module
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment