99
1010using AutoProperties ;
1111
12+ using PropertyChanged ;
13+
1214using ResXManager . Infrastructure ;
1315
1416using Throttle ;
@@ -25,44 +27,47 @@ public sealed class ForceGlobalAttribute : Attribute
2527/// </summary>
2628public abstract class ConfigurationBase : INotifyPropertyChanged
2729{
28- private const string FileName = "Configuration.xml" ;
30+ private const string GlobalConfigFileName = "Configuration.xml" ;
31+ private const string SolutionConfigFileName = "ResXManager.config.xml" ;
2932
30- private static readonly string _directory = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) , "tom-englert.de" , "ResXManager" ) ;
33+ private static readonly string _appDataFolder = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) , "tom-englert.de" , "ResXManager" ) ;
3134
32- private readonly string _filePath ;
33- private readonly XmlConfiguration _configuration ;
35+ private readonly string _globalConfigFilePath ;
36+ private readonly XmlConfiguration _globalConfiguration ;
3437 private readonly Dictionary < string , object > _cachedObjects = new ( ) ;
3538
39+ private XmlConfiguration ? _solutionConfiguration ;
40+ private string ? _solutionConfigFilePath ;
41+ private readonly PropertyInfo [ ] _allPublicProperties ;
42+
3643 protected ConfigurationBase ( ITracer tracer )
3744 {
3845 Tracer = tracer ;
39- _filePath = Path . Combine ( _directory , FileName ) ;
40-
41- try
42- {
43- Directory . CreateDirectory ( _directory ) ;
44-
45- using var reader = new StreamReader ( File . OpenRead ( _filePath ) ) ;
46+ _allPublicProperties = GetType ( ) . GetProperties ( BindingFlags . Public | BindingFlags . Instance ) ;
4647
47- _configuration = new XmlConfiguration ( tracer , reader ) ;
48+ _globalConfigFilePath = Path . Combine ( _appDataFolder , GlobalConfigFileName ) ;
49+ _globalConfiguration = LoadConfiguration ( _globalConfigFilePath , tracer ) ;
50+ }
4851
49- return ;
50- }
51- catch
52- {
53- // can't read configuration, just go with default.
54- }
52+ [ InterceptIgnore ]
53+ public bool IsScopeSupported => true ;
5554
56- _configuration = new XmlConfiguration ( tracer ) ;
57- }
55+ [ InterceptIgnore ]
56+ public ConfigurationScope Scope { get ; private set ; }
5857
59- public abstract bool IsScopeSupported { get ; }
58+ [ InterceptIgnore ]
59+ public XmlConfiguration EffectiveConfiguration => _solutionConfiguration ?? _globalConfiguration ;
6060
61- public abstract ConfigurationScope Scope { get ; }
61+ [ InterceptIgnore ]
62+ public string EffectiveConfigFilePath => _solutionConfigFilePath ?? _globalConfigFilePath ;
6263
6364 [ InterceptIgnore ]
6465 protected ITracer Tracer { get ; }
6566
67+ [ InterceptIgnore ]
68+ [ OnChangedMethod ( nameof ( OnSolutionFolderChanged ) ) ]
69+ public string ? SolutionFolder { get ; set ; }
70+
6671 [ GetInterceptor ]
6772 protected T ? GetProperty < T > ( string key , PropertyInfo propertyInfo )
6873 {
@@ -107,7 +112,7 @@ protected ConfigurationBase(ITracer tracer)
107112
108113 protected virtual T ? InternalGetValue < T > ( T ? defaultValue , string key )
109114 {
110- return ConvertFromString ( _configuration . GetValue ( key , null ) , defaultValue ) ;
115+ return ConvertFromString ( EffectiveConfiguration . GetValue ( key , null ) , defaultValue ) ;
111116 }
112117
113118 [ SetInterceptor ]
@@ -122,7 +127,9 @@ protected virtual void InternalSetValue<T>(T? value, string key, bool forceGloba
122127 {
123128 try
124129 {
125- _configuration . SetValue ( key , ConvertToString ( value ) ) ;
130+ var configuration = forceGlobal ? _globalConfiguration : EffectiveConfiguration ;
131+
132+ configuration . SetValue ( key , ConvertToString ( value ) ) ;
126133
127134 Save ( ) ;
128135 }
@@ -137,13 +144,13 @@ private void Save()
137144 {
138145 try
139146 {
140- using var writer = new StreamWriter ( File . Create ( _filePath ) ) ;
147+ using var writer = new StreamWriter ( File . Create ( EffectiveConfigFilePath ) ) ;
141148
142- _configuration . Save ( writer ) ;
149+ EffectiveConfiguration . Save ( writer ) ;
143150 }
144151 catch ( Exception ex )
145152 {
146- Tracer . TraceError ( $ "Fatal error writing configuration file: { _filePath } - { ex . Message } ") ;
153+ Tracer . TraceError ( $ "Fatal error writing configuration file: { EffectiveConfigFilePath } - { ex . Message } ") ;
147154 }
148155 }
149156
@@ -212,6 +219,50 @@ private static TypeConverter GetTypeConverter(Type type)
212219
213220 protected virtual void OnPropertyChanged ( string propertyName )
214221 {
215- PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( propertyName ) ) ;
222+ PropertyChanged ? . Invoke ( this , new ( propertyName ) ) ;
223+ }
224+
225+ private void OnSolutionFolderChanged ( string oldValue , string newValue )
226+ {
227+ if ( newValue . IsNullOrEmpty ( ) )
228+ {
229+ _solutionConfigFilePath = null ;
230+ _solutionConfiguration = null ;
231+ Scope = ConfigurationScope . Global ;
232+ }
233+ else
234+ {
235+ _solutionConfigFilePath = Path . Combine ( newValue , SolutionConfigFileName ) ;
236+ _solutionConfiguration = LoadConfiguration ( _solutionConfigFilePath , Tracer ) ;
237+ Scope = ConfigurationScope . Solution ;
238+ }
239+
240+ NotifyAll ( ) ;
241+ }
242+
243+ private static XmlConfiguration LoadConfiguration ( string filePath , ITracer tracer )
244+ {
245+ try
246+ {
247+ Directory . CreateDirectory ( Path . GetDirectoryName ( filePath ) ) ;
248+
249+ using var reader = new StreamReader ( File . OpenRead ( filePath ) ) ;
250+
251+ return new ( tracer , reader ) ;
252+ }
253+ catch
254+ {
255+ // can't read configuration, just go with default.
256+ }
257+
258+ return new ( tracer ) ;
259+ }
260+
261+ private void NotifyAll ( )
262+ {
263+ foreach ( var property in _allPublicProperties )
264+ {
265+ OnPropertyChanged ( property . Name ) ;
266+ }
216267 }
217268}
0 commit comments