Last active
December 29, 2015 17:19
-
-
Save peace2048/7702985 to your computer and use it in GitHub Desktop.
ある `INotifyPropertyChanged` なオブジェクトのプロパティが変化したら 別のオブジェクトのプロパティも更新する
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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