Skip to content

Commit

Permalink
INPC support
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivan G committed Nov 29, 2019
1 parent d6d6ac0 commit 03dadc3
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 8 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Config.Net [![NuGet](https://img.shields.io/nuget/v/Config.Net.svg)](https://www.nuget.org/packages/Config.Net) [![Build status](https://aloneguid.visualstudio.com/AllPublic/_apis/build/status/Config.Net)](https://aloneguid.visualstudio.com/AllPublic/_build/latest?definitionId=47) ![](https://img.shields.io/azure-devops/tests/aloneguid/AllPublic/47.svg) [![open collective backers and sponsors](https://img.shields.io/opencollective/all/config.svg)](https://opencollective.com/config)
# Config.Net [![NuGet](https://img.shields.io/nuget/v/Config.Net.svg)](https://www.nuget.org/packages/Config.Net) [![Build status](https://aloneguid.visualstudio.com/AllPublic/_apis/build/status/Config.Net)](https://aloneguid.visualstudio.com/AllPublic/_build/latest?definitionId=66) ![](https://img.shields.io/azure-devops/tests/aloneguid/AllPublic/66.svg) [![open collective backers and sponsors](https://img.shields.io/opencollective/all/config.svg)](https://opencollective.com/config)

A comprehensive, easy to use and powerful .NET configuration library, fully covered with unit tests and tested in the wild on thousands of servers and applications.

Expand All @@ -23,6 +23,7 @@ This library eliminates the problem of having configuration in different places,
- [Nested Interfaces](doc/NestedInterfaces.md)
- [Collections](doc/Collections.md)
- [Binding to Interface Methods](doc/DynamicConfiguration.md)
- [INotifyPropertyChanged](doc/INotifyPropertyChanged.md)
- Extending Config.Net
- [Implementing a custom parser](doc/CustomParsers.md)
- [Implementing a custom configuration store](doc/Stores_Custom.md)
Expand Down
13 changes: 8 additions & 5 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ steps:
OverWrite: true
flattenFolders: true

#- task: PublishBuildArtifacts@1
# condition: "eq(variables['Build.SourceBranch'], 'refs/heads/master')"
# displayName: 'publish nugets'
# inputs:
# ArtifactName: nuget
- task: NuGetCommand@2
displayName: 'publish to nuget.org'
condition: "eq(variables['Build.SourceBranch'], 'refs/heads/master')"
inputs:
command: push
packagesToPush: '$(build.artifactstagingdirectory)/*.nupkg'
nuGetFeedType: external
publishFeedCredentials: 'nuget.org (aloneguid)'
27 changes: 27 additions & 0 deletions doc/INotifyPropertyChanged.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# INotifyPropertyChanged Support

INotifyPropertyChanged is part of .NET Framework and is often ised in situations when you want to monitor changes to a class' property. It is also an essential part of **Xamarin**, **WPF**, **UWP**, and **Windows Forms** data binding systems.

Config.Net totally supports `INPC` interface out of the box, and all you need to do is derive your interface from `INPC`:

```csharp
public interface IMyConfiguration : INotifyPropertyChanged
{
string Name { get; set; }
}
```

then build your configuration as usual and subscribe to property changed event:

```csharp
IMyConfiguration config = new ConfigurationBuilder<IMyConfiguration>()
//...
.Build();

config.PropertyChanged += (sender, e) =>
{
Assert.Equal("Name", e.PropertyName);
};

config.Name = "test"; //this will trigger PropertyChanged delegate
```
67 changes: 67 additions & 0 deletions src/Config.Net.Tests/NotifyPropertyChangedTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using Config.Net.Stores;
using Xunit;

namespace Config.Net.Tests
{
public class NotifyPropertyChangedTest
{
private INPC _interface;
private DictionaryConfigStore _store;

public NotifyPropertyChangedTest()
{
_store = new DictionaryConfigStore();

_interface = new ConfigurationBuilder<INPC>()
.UseConfigStore(_store)
.Build();
}

[Fact]
public void Change_property_calls_notify()
{
bool isSet = false;
string nameSet = null;

_interface.PropertyChanged += (sender, e) =>
{
isSet = true;
nameSet = e.PropertyName;
};

_interface.Name = "test";
Assert.True(isSet);
Assert.Equal("Name", nameSet);
}

[Fact]
public void Change_aliased_property_calls_notify()
{
bool isSet = false;
string nameSet = null;

_interface.PropertyChanged += (sender, e) =>
{
isSet = true;
nameSet = e.PropertyName;
};

_interface.AliasedName = "test";
Assert.True(isSet);
Assert.Equal("Name1", nameSet);
}
}

public interface INPC : INotifyPropertyChanged
{
string Name { get; set; }

[Option(Alias = "Name1")]
string AliasedName { get; set; }
}

}
1 change: 1 addition & 0 deletions src/Config.Net.sln
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{CDB02CCC-51F
..\doc\CustomParsers.md = ..\doc\CustomParsers.md
..\doc\DynamicConfiguration.md = ..\doc\DynamicConfiguration.md
..\doc\flatline.md = ..\doc\flatline.md
..\doc\INotifyPropertyChanged.md = ..\doc\INotifyPropertyChanged.md
..\doc\jetbrains_rider.png = ..\doc\jetbrains_rider.png
..\doc\jetbrains_rider_small.png = ..\doc\jetbrains_rider_small.png
..\doc\NestedInterfaces.md = ..\doc\NestedInterfaces.md
Expand Down
41 changes: 39 additions & 2 deletions src/Config.Net/Core/Interfacenterceptor.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Text;
using Castle.DynamicProxy;
Expand All @@ -14,6 +15,8 @@ class InterfaceInterceptor : IInterceptor
private readonly string _prefix;
private readonly DynamicReader _reader;
private readonly DynamicWriter _writer;
private readonly bool _isInpc;
private PropertyChangedEventHandler _inpcHandler;

public InterfaceInterceptor(Type interfaceType, IoHandler ioHandler, string prefix = null)
{
Expand All @@ -22,6 +25,7 @@ public InterfaceInterceptor(Type interfaceType, IoHandler ioHandler, string pref
_prefix = prefix;
_reader = new DynamicReader(prefix, ioHandler);
_writer = new DynamicWriter(prefix, ioHandler);
_isInpc = interfaceType.GetInterface(nameof(INotifyPropertyChanged)) != null;
}

private ResultBox FindBox(IInvocation invocation)
Expand All @@ -39,11 +43,13 @@ private ResultBox FindBox(IInvocation invocation)

public void Intercept(IInvocation invocation)
{
if (TryInterceptInpc(invocation)) return;

ResultBox rbox = FindBox(invocation);

bool isRead =
(rbox is PropertyResultBox pbox && PropertyResultBox.IsGetProperty(invocation.Method)) ||
(rbox is ProxyResultBox xbox && PropertyResultBox.IsGetProperty(invocation.Method)) ||
(rbox is PropertyResultBox && PropertyResultBox.IsGetProperty(invocation.Method)) ||
(rbox is ProxyResultBox && PropertyResultBox.IsGetProperty(invocation.Method)) ||
(rbox is MethodResultBox mbox && mbox.IsGettter) ||
(rbox is CollectionResultBox);

Expand All @@ -55,7 +61,38 @@ public void Intercept(IInvocation invocation)
else
{
_writer.Write(rbox, invocation.Arguments);

TryNotifyInpc(invocation, rbox);
}
}

private bool TryInterceptInpc(IInvocation invocation)
{
if (!_isInpc) return false;

if (invocation.Method.Name == "add_PropertyChanged")
{
invocation.ReturnValue =
_inpcHandler =
(PropertyChangedEventHandler)Delegate.Combine(_inpcHandler, (Delegate)invocation.Arguments[0]);
return true;
}
else if(invocation.Method.Name == "remove_PropertyChanged")
{
invocation.ReturnValue =
_inpcHandler =
(PropertyChangedEventHandler)Delegate.Remove(_inpcHandler, (Delegate)invocation.Arguments[0]);
return true;
}

return false;
}

private void TryNotifyInpc(IInvocation invocation, ResultBox rbox)
{
if (_inpcHandler == null || rbox is MethodResultBox) return;

_inpcHandler.Invoke(invocation.InvocationTarget, new PropertyChangedEventArgs(rbox.StoreByName));
}
}
}

0 comments on commit 03dadc3

Please sign in to comment.