[NUI] Fix build warning CA1507
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / public / XamlBinding / BindableProperty.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Linq.Expressions;
5 using System.Reflection;
6 using System.ComponentModel;
7 using Tizen.NUI.Binding.Internals;
8 using Tizen.NUI.Xaml;
9
10 namespace Tizen.NUI.Binding
11 {
12     /// <summary>
13     /// A BindableProperty is a backing store for properties allowing bindings on BindableObject.
14     /// </summary>
15     [DebuggerDisplay("{PropertyName}")]
16     [TypeConverter(typeof(BindablePropertyConverter))]
17     [EditorBrowsable(EditorBrowsableState.Never)]
18     public sealed class BindableProperty
19     {
20         /// <summary>
21         /// Delegate for BindableProperty.PropertyChanged.
22         /// </summary>
23         /// <param name="bindable">The bindable object that contains the property.</param>
24         /// <param name="oldValue">The old property value.</param>
25         /// <param name="newValue">The new property value.</param>
26         public delegate void BindingPropertyChangedDelegate(BindableObject bindable, object oldValue, object newValue);
27
28         /// <summary>
29         /// Strongly-typed delegate for BindableProperty.PropertyChanged.
30         /// </summary>
31         /// <typeparam name="TPropertyType">The type of the bound property.</typeparam>
32         /// <param name="bindable">The bindable object that contains the property.</param>
33         /// <param name="oldValue">The old property value.</param>
34         /// <param name="newValue">The new property value.</param>
35         public delegate void BindingPropertyChangedDelegate<in TPropertyType>(BindableObject bindable, TPropertyType oldValue, TPropertyType newValue);
36
37         /// <summary>
38         /// Delegate for BindableProperty.PropertyChanging.
39         /// </summary>
40         /// <param name="bindable">The bindable object that contains the property.</param>
41         /// <param name="oldValue">The old property value.</param>
42         /// <param name="newValue">The new property value.</param>
43         public delegate void BindingPropertyChangingDelegate(BindableObject bindable, object oldValue, object newValue);
44
45         /// <summary>
46         /// Strongly-typed delegate for BindableProperty.PropertyChanging.
47         /// </summary>
48         /// <typeparam name="TPropertyType">The type of the bound property.</typeparam>
49         /// <param name="bindable">The bindable object that contains the property.</param>
50         /// <param name="oldValue">The old property value.</param>
51         /// <param name="newValue">The new property value.</param>
52         public delegate void BindingPropertyChangingDelegate<in TPropertyType>(BindableObject bindable, TPropertyType oldValue, TPropertyType newValue);
53
54         /// <summary>
55         /// Delegate for BindableProperty.CoerceValue.
56         /// </summary>
57         /// <param name="bindable">The bindable object that contains the property.</param>
58         /// <param name="value">The value to coerce.</param>
59         /// <returns>System.Object</returns>
60         public delegate object CoerceValueDelegate(BindableObject bindable, object value);
61
62         /// <summary>
63         /// Strongly-typed delegate for BindableProperty.CoerceValue.
64         /// </summary>
65         /// <typeparam name="TPropertyType">The type of the bound property.</typeparam>
66         /// <param name="bindable">The bindable object that contains the property.</param>
67         /// <param name="value">The value to coerce.</param>
68         /// <returns>TPropertyType</returns>
69         public delegate TPropertyType CoerceValueDelegate<TPropertyType>(BindableObject bindable, TPropertyType value);
70
71         /// <summary>
72         /// Delegate for BindableProperty.DefaultValueCreator.
73         /// </summary>
74         /// <param name="bindable">The bindable object that contains the property.</param>
75         /// <returns>System.Object</returns>
76         public delegate object CreateDefaultValueDelegate(BindableObject bindable);
77
78         /// <summary>
79         /// Strongly-typed delegate for BindableProperty.DefaultValueCreator.
80         /// </summary>
81         /// <typeparam name="TDeclarer">The type of the object that delared the property.</typeparam>
82         /// <typeparam name="TPropertyType">The type of the bound property.</typeparam>
83         /// <param name="bindable">The bindable object that contains the property.</param>
84         /// <returns>TPropertyType</returns>
85         public delegate TPropertyType CreateDefaultValueDelegate<in TDeclarer, out TPropertyType>(TDeclarer bindable);
86
87         /// <summary>
88         /// Delegate for BindableProperty.ValidateValue.
89         /// </summary>
90         /// <param name="bindable">The bindable object that contains the property.</param>
91         /// <param name="value">The default value.</param>
92         /// <returns>System.Boolean</returns>
93         public delegate bool ValidateValueDelegate(BindableObject bindable, object value);
94
95         /// <summary>
96         /// Strongly-typed delegate for BindableProperty.ValidateValue.
97         /// </summary>
98         /// <typeparam name="TPropertyType">The type of the bound property.</typeparam>
99         /// <param name="bindable">The bindable object that contains the property.</param>
100         /// <param name="value">The default value.</param>
101         /// <returns>System.Boolean</returns>
102         public delegate bool ValidateValueDelegate<in TPropertyType>(BindableObject bindable, TPropertyType value);
103                 
104         //To confirm the static dictionary will be created before the constructor is called.
105         static BindableProperty()
106         {
107         }
108
109         static readonly Dictionary<Type, TypeConverter> WellKnownConvertTypes = new  Dictionary<Type,TypeConverter>
110         {
111             { typeof(Uri), new UriTypeConverter() },
112             { typeof(Color), new ColorTypeConverter() },
113             { typeof(Size2D), new Size2DTypeConverter() },
114             { typeof(Position2D), new Position2DTypeConverter() },
115             { typeof(Size), new SizeTypeConverter() },
116             { typeof(Position), new PositionTypeConverter() },
117             { typeof(Rectangle), new RectangleTypeConverter() },
118             { typeof(Rotation), new RotationTypeConverter() },
119             { typeof(Vector2), new Vector2TypeConverter() },
120             { typeof(Vector3), new Vector3TypeConverter() },
121             { typeof(Vector4), new Vector4TypeConverter() },
122             { typeof(RelativeVector2), new RelativeVector2TypeConverter() },
123             { typeof(RelativeVector3), new RelativeVector3TypeConverter() },
124             { typeof(RelativeVector4), new RelativeVector4TypeConverter() },
125         };
126
127         //Modification for NUI XAML : user defined converter for DynamicResource can be added
128         static internal Dictionary<Type, TypeConverter> UserCustomConvertTypes = new Dictionary<Type, TypeConverter>
129         {
130         };
131
132         // more or less the encoding of this, without the need to reflect
133         // http://msdn.microsoft.com/en-us/library/y5b434w4.aspx
134         static readonly Dictionary<Type, Type[]> SimpleConvertTypes = new Dictionary<Type, Type[]>
135         {
136             { typeof(sbyte), new[] { typeof(string), typeof(short), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
137             { typeof(byte), new[] { typeof(string), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
138             { typeof(short), new[] { typeof(string), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
139             { typeof(ushort), new[] { typeof(string), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
140             { typeof(int), new[] { typeof(string), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
141             { typeof(uint), new[] { typeof(string), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
142             { typeof(long), new[] { typeof(string), typeof(float), typeof(double), typeof(decimal) } },
143             { typeof(char), new[] { typeof(string), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
144             { typeof(float), new[] { typeof(string), typeof(double) } },
145             { typeof(ulong), new[] { typeof(string), typeof(float), typeof(double), typeof(decimal) } }
146         };
147
148         BindableProperty(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
149                                  ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
150                                  CoerceValueDelegate coerceValue = null, BindablePropertyBindingChanging bindingChanging = null, bool isReadOnly = false, CreateDefaultValueDelegate defaultValueCreator = null)
151         {
152             if (propertyName == null)
153                 throw new ArgumentNullException(nameof(propertyName));
154             if (ReferenceEquals(returnType, null))
155                 throw new ArgumentNullException(nameof(returnType));
156             if (ReferenceEquals(declaringType, null))
157                 throw new ArgumentNullException(nameof(declaringType));
158
159             // don't use Enum.IsDefined as its redonkulously expensive for what it does
160             if (defaultBindingMode != BindingMode.Default && defaultBindingMode != BindingMode.OneWay && defaultBindingMode != BindingMode.OneWayToSource && defaultBindingMode != BindingMode.TwoWay && defaultBindingMode != BindingMode.OneTime)
161                 throw new ArgumentException("Not a valid type of BindingMode", nameof(defaultBindingMode));
162             if (defaultValue == null && Nullable.GetUnderlyingType(returnType) == null && returnType.GetTypeInfo().IsValueType)
163                 throw new ArgumentException("Not a valid default value", nameof(defaultValue));
164             if (defaultValue != null && !returnType.IsInstanceOfType(defaultValue))
165                 throw new ArgumentException("Default value did not match return type", nameof(defaultValue));
166             if (defaultBindingMode == BindingMode.Default)
167                 defaultBindingMode = BindingMode.OneWay;
168
169             PropertyName = propertyName;
170             ReturnType = returnType;
171             ReturnTypeInfo = returnType.GetTypeInfo();
172             DeclaringType = declaringType;
173             DefaultValue = defaultValue;
174             DefaultBindingMode = defaultBindingMode;
175             PropertyChanged = propertyChanged;
176             PropertyChanging = propertyChanging;
177             ValidateValue = validateValue;
178             CoerceValue = coerceValue;
179             BindingChanging = bindingChanging;
180             IsReadOnly = isReadOnly;
181             DefaultValueCreator = defaultValueCreator;
182
183             Dictionary<string, BindableProperty> nameToBindableProperty;
184             bindablePropertyOfType.TryGetValue(declaringType, out nameToBindableProperty);
185             if (null == nameToBindableProperty)
186             {
187                 nameToBindableProperty = new Dictionary<string, BindableProperty>();
188                 bindablePropertyOfType.Add(declaringType, nameToBindableProperty);
189             }
190
191             if (!nameToBindableProperty.ContainsKey(propertyName))
192             {
193                 nameToBindableProperty.Add(propertyName, this);
194             }
195             else
196             {
197                 nameToBindableProperty[propertyName] = this;
198             }
199         }
200
201         private static bool AddParentTypeProperty(Type type, Dictionary<string, BindableProperty> propertyDict)
202         {
203             bool ret = false;
204
205             if (null != type)
206             {
207                 Dictionary<string, BindableProperty> nameToBindableProperty;
208                 bindablePropertyOfType.TryGetValue(type, out nameToBindableProperty);
209
210                 if (null != nameToBindableProperty)
211                 {
212                     ret = true;
213
214                     foreach (KeyValuePair<string, BindableProperty> keyValuePair in nameToBindableProperty)
215                     {
216                         if (!propertyDict.ContainsKey(keyValuePair.Key))
217                         {
218                             propertyDict.Add(keyValuePair.Key, keyValuePair.Value);
219                         }
220                     }
221                 }
222
223                 if (true == AddParentTypeProperty(type.BaseType, propertyDict))
224                 {
225                     ret = true;
226                 }
227             }
228
229             return ret;
230         }
231
232         static internal Dictionary<Type, Dictionary<string, BindableProperty>> bindablePropertyOfType = new Dictionary<Type, Dictionary<string, BindableProperty>>();
233         static private HashSet<Type> baseTypePropertyHasBeenAdded = new HashSet<Type>();
234
235         static internal void GetBindablePropertysOfType(Type type, out Dictionary<string, BindableProperty> dictionary)
236         {
237             dictionary = null;
238
239             bindablePropertyOfType.TryGetValue(type, out dictionary);
240
241             if (!baseTypePropertyHasBeenAdded.Contains(type))
242             {
243                 bool isCurDictNull = false;
244
245                 if (null == dictionary)
246                 {
247                     isCurDictNull = true;
248                     dictionary = new Dictionary<string, BindableProperty>();
249                 }
250
251                 if (true == AddParentTypeProperty(type.BaseType, dictionary) && true == isCurDictNull)
252                 {
253                     bindablePropertyOfType.Add(type, dictionary);
254                 }
255
256                 baseTypePropertyHasBeenAdded.Add(type);
257             }
258         }
259
260         /// <summary>
261         /// Gets the type declaring the BindableProperty.
262         /// </summary>
263         public Type DeclaringType { get; private set; }
264
265         /// <summary>
266         /// Gets the default BindingMode.
267         /// </summary>
268         public BindingMode DefaultBindingMode { get; private set; }
269
270         /// <summary>
271         /// Gets the default value for the BindableProperty.
272         /// </summary>
273         public object DefaultValue { get; }
274
275         /// <summary>
276         /// Gets a value indicating if the BindableProperty is created form a BindablePropertyKey.
277         /// </summary>
278         public bool IsReadOnly { get; private set; }
279
280         /// <summary>
281         /// Gets the property name.
282         /// </summary>
283         public string PropertyName { get; }
284
285         /// <summary>
286         /// Gets the type of the BindableProperty.
287         /// </summary>
288         public Type ReturnType { get; }
289
290         internal BindablePropertyBindingChanging BindingChanging { get; private set; }
291
292         internal CoerceValueDelegate CoerceValue { get; private set; }
293
294         internal CreateDefaultValueDelegate DefaultValueCreator { get; }
295
296         internal BindingPropertyChangedDelegate PropertyChanged { get; private set; }
297
298         internal BindingPropertyChangingDelegate PropertyChanging { get; private set; }
299
300         internal System.Reflection.TypeInfo ReturnTypeInfo { get; }
301
302         internal ValidateValueDelegate ValidateValue { get; private set; }
303
304         /// <summary>
305         /// Deprecated. Do not use.
306         /// </summary>
307         /// <typeparam name="TDeclarer">The type of the declaring object.</typeparam>
308         /// <typeparam name="TPropertyType">The type of the property.</typeparam>
309         /// <param name="getter">An expression identifying the getter for the property using this BindableProperty as backing store.</param>
310         /// <param name="defaultValue">The default value for the property.</param>
311         /// <param name="defaultBindingMode">The BindingMode to use on SetBinding() if no BindingMode is given. This parameter is optional. Default is BindingMode.OneWay.</param>
312         /// <param name="validateValue">A delegate to be run when a value is set. This parameter is optional. Default is null.</param>
313         /// <param name="propertyChanged">A delegate to be run when the value has changed. This parameter is optional. Default is null.</param>
314         /// <param name="propertyChanging">A delegate to be run when the value will change. This parameter is optional. Default is null.</param>
315         /// <param name="coerceValue">A delegate used to coerce the range of a value. This parameter is optional. Default is null.</param>
316         /// <param name="defaultValueCreator">A Func used to initialize default value for reference types.</param>
317         /// <returns>A newly created BindableProperty.</returns>
318         [Obsolete("Create<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
319         public static BindableProperty Create<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
320                                                                         ValidateValueDelegate<TPropertyType> validateValue = null, BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null,
321                                                                         BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, CoerceValueDelegate<TPropertyType> coerceValue = null,
322                                                                         CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject
323         {
324             return Create(getter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, defaultValueCreator: defaultValueCreator);
325         }
326
327         /// <summary>
328         /// Creates a new instance of the BindableProperty class.
329         /// </summary>
330         /// <param name="propertyName">The name of the BindableProperty.</param>
331         /// <param name="returnType">The type of the property.</param>
332         /// <param name="declaringType">The type of the declaring object.</param>
333         /// <param name="defaultValue">The default value for the property.</param>
334         /// <param name="defaultBindingMode">The BindingMode to use on SetBinding() if no BindingMode is given. This parameter is optional. Default is BindingMode.OneWay.</param>
335         /// <param name="validateValue">A delegate to be run when a value is set. This parameter is optional. Default is null.</param>
336         /// <param name="propertyChanged">A delegate to be run when the value has changed. This parameter is optional. Default is null.</param>
337         /// <param name="propertyChanging">A delegate to be run when the value will change. This parameter is optional. Default is null.</param>
338         /// <param name="coerceValue">A delegate used to coerce the range of a value. This parameter is optional. Default is null.</param>
339         /// <param name="defaultValueCreator">A Func used to initialize default value for reference types.</param>
340         /// <returns>A newly created BindableProperty.</returns>
341         public static BindableProperty Create(string propertyName, Type returnType, Type declaringType, object defaultValue = null, BindingMode defaultBindingMode = BindingMode.OneWay,
342                                               ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
343                                               CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
344         {
345             return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue,
346                 defaultValueCreator: defaultValueCreator);
347         }
348
349         /// <summary>
350         /// Deprecated. Do not use.
351         /// </summary>
352         /// <typeparam name="TDeclarer">The type of the declaring object.</typeparam>
353         /// <typeparam name="TPropertyType">The type of the property.</typeparam>
354         /// <param name="staticgetter">An expression identifying a static method returning the value of the property using this BindableProperty as backing store.</param>
355         /// <param name="defaultValue">The default value for the property.</param>
356         /// <param name="defaultBindingMode">The BindingMode to use on SetBinding() if no BindingMode is given. This parameter is optional. Default is BindingMode.OneWay.</param>
357         /// <param name="validateValue">A delegate to be run when a value is set. This parameter is optional. Default is null.</param>
358         /// <param name="propertyChanged">A delegate to be run when the value has changed. This parameter is optional. Default is null.</param>
359         /// <param name="propertyChanging">A delegate to be run when the value will change. This parameter is optional. Default is null.</param>
360         /// <param name="coerceValue">A delegate used to coerce the range of a value. This parameter is optional. Default is null.</param>
361         /// <param name="defaultValueCreator">A Func used to initialize default value for reference types.</param>
362         [Obsolete("CreateAttached<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
363         public static BindableProperty CreateAttached<TDeclarer, TPropertyType>(Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue,
364                                                                                 BindingMode defaultBindingMode = BindingMode.OneWay, ValidateValueDelegate<TPropertyType> validateValue = null, BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null,
365                                                                                 BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null, CoerceValueDelegate<TPropertyType> coerceValue = null,
366                                                                                 CreateDefaultValueDelegate<BindableObject, TPropertyType> defaultValueCreator = null)
367         {
368             return CreateAttached<TDeclarer, TPropertyType>(staticgetter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null,
369                 defaultValueCreator: defaultValueCreator);
370         }
371
372         /// <summary>
373         /// Creates a new instance of the BindableProperty class for an attached property.
374         /// </summary>
375         /// <param name="propertyName">The name of the BindableProperty.</param>
376         /// <param name="returnType">The type of the property.</param>
377         /// <param name="declaringType">The type of the declaring object.</param>
378         /// <param name="defaultValue">The default value for the property.</param>
379         /// <param name="defaultBindingMode">The BindingMode to use on SetBinding() if no BindingMode is given. This parameter is optional. Default is BindingMode.OneWay.</param>
380         /// <param name="validateValue">A delegate to be run when a value is set. This parameter is optional. Default is null.</param>
381         /// <param name="propertyChanged">A delegate to be run when the value has changed. This parameter is optional. Default is null.</param>
382         /// <param name="propertyChanging">A delegate to be run when the value will change. This parameter is optional. Default is null.</param>
383         /// <param name="coerceValue">A delegate used to coerce the range of a value. This parameter is optional. Default is null.</param>
384         /// <param name="defaultValueCreator">A Func used to initialize default value for reference types.</param>
385         /// <returns>A newly created BindableProperty.</returns>
386         public static BindableProperty CreateAttached(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWay,
387                                                       ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
388                                                       CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
389         {
390             return CreateAttached(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, false, defaultValueCreator);
391         }
392
393         /// <summary>
394         /// Deprecated. Do not use.
395         /// </summary>
396         /// <typeparam name="TDeclarer">The type of the declaring object.</typeparam>
397         /// <typeparam name="TPropertyType">The type of the property.</typeparam>
398         /// <param name="staticgetter">An expression identifying a static method returning the value of the property using this BindableProperty as backing store.</param>
399         /// <param name="defaultValue">The default value for the property.</param>
400         /// <param name="defaultBindingMode">The BindingMode to use on SetBinding() if no BindingMode is given. This parameter is optional. Default is BindingMode.OneWay.</param>
401         /// <param name="validateValue">A delegate to be run when a value is set. This parameter is optional. Default is null.</param>
402         /// <param name="propertyChanged">A delegate to be run when the value has changed. This parameter is optional. Default is null.</param>
403         /// <param name="propertyChanging">A delegate to be run when the value will change. This parameter is optional. Default is null.</param>
404         /// <param name="coerceValue">A delegate used to coerce the range of a value. This parameter is optional. Default is null.</param>
405         /// <param name="defaultValueCreator">A Func used to initialize default value for reference types.</param>
406         /// <returns>A newly created attached read-only BindablePropertyKey.</returns>
407         [Obsolete("CreateAttachedReadOnly<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
408         public static BindablePropertyKey CreateAttachedReadOnly<TDeclarer, TPropertyType>(Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue,
409                                                                                            BindingMode defaultBindingMode = BindingMode.OneWayToSource, ValidateValueDelegate<TPropertyType> validateValue = null,
410                                                                                            BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null,
411                                                                                            CoerceValueDelegate<TPropertyType> coerceValue = null, CreateDefaultValueDelegate<BindableObject, TPropertyType> defaultValueCreator = null)
412
413         {
414             return
415                 new BindablePropertyKey(CreateAttached<TDeclarer, TPropertyType>(staticgetter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true,
416                     defaultValueCreator));
417         }
418
419         /// <summary>
420         /// Creates a new instance of the BindableProperty class for attached read-only properties.
421         /// </summary>
422         /// <param name="propertyName">The name of the BindableProperty.</param>
423         /// <param name="returnType">The type of the property.</param>
424         /// <param name="declaringType">The type of the declaring object.</param>
425         /// <param name="defaultValue">The default value for the property.</param>
426         /// <param name="defaultBindingMode">The BindingMode to use on SetBinding() if no BindingMode is given. This parameter is optional. Default is BindingMode.OneWay.</param>
427         /// <param name="validateValue">A delegate to be run when a value is set. This parameter is optional. Default is null.</param>
428         /// <param name="propertyChanged">A delegate to be run when the value has changed. This parameter is optional. Default is null.</param>
429         /// <param name="propertyChanging">A delegate to be run when the value will change. This parameter is optional. Default is null.</param>
430         /// <param name="coerceValue">A delegate used to coerce the range of a value. This parameter is optional. Default is null.</param>
431         /// <param name="defaultValueCreator">A Func used to initialize default value for reference types.</param>
432         /// <returns>A newly created attached read-only BindablePropertyKey.</returns>
433         public static BindablePropertyKey CreateAttachedReadOnly(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource,
434                                                                  ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
435                                                                  CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
436         {
437             return
438                 new BindablePropertyKey(CreateAttached(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true,
439                     defaultValueCreator));
440         }
441
442         /// <summary>
443         /// Deprecated. Do not use.
444         /// </summary>
445         /// <typeparam name="TDeclarer">The type of the declaring object.</typeparam>
446         /// <typeparam name="TPropertyType">The type of the property.</typeparam>
447         /// <param name="getter">An expression identifying the getter for the property using this BindableProperty as backing store.</param>
448         /// <param name="defaultValue">The default value for the property.</param>
449         /// <param name="defaultBindingMode">The BindingMode to use on SetBinding() if no BindingMode is given. This parameter is optional. Default is BindingMode.OneWay.</param>
450         /// <param name="validateValue">A delegate to be run when a value is set. This parameter is optional. Default is null.</param>
451         /// <param name="propertyChanged">A delegate to be run when the value has changed. This parameter is optional. Default is null.</param>
452         /// <param name="propertyChanging">A delegate to be run when the value will change. This parameter is optional. Default is null.</param>
453         /// <param name="coerceValue">A delegate used to coerce the range of a value. This parameter is optional. Default is null.</param>
454         /// <param name="defaultValueCreator">A Func used to initialize default value for reference types.</param>
455         /// <returns>A newly created BindablePropertyKey.</returns>
456         [Obsolete("CreateReadOnly<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
457         public static BindablePropertyKey CreateReadOnly<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue,
458                                                                                    BindingMode defaultBindingMode = BindingMode.OneWayToSource, ValidateValueDelegate<TPropertyType> validateValue = null,
459                                                                                    BindingPropertyChangedDelegate<TPropertyType> propertyChanged = null, BindingPropertyChangingDelegate<TPropertyType> propertyChanging = null,
460                                                                                    CoerceValueDelegate<TPropertyType> coerceValue = null, CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject
461         {
462             return new BindablePropertyKey(Create(getter, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, null, true, defaultValueCreator));
463         }
464
465         /// <summary>
466         /// Creates a new instance of the BindablePropertyKey class.
467         /// </summary>
468         /// <param name="propertyName">The name of the BindableProperty.</param>
469         /// <param name="returnType">The type of the property.</param>
470         /// <param name="declaringType">The type of the declaring object.</param>
471         /// <param name="defaultValue">The default value for the property.</param>
472         /// <param name="defaultBindingMode">The BindingMode to use on SetBinding() if no BindingMode is given. This parameter is optional. Default is BindingMode.OneWay.</param>
473         /// <param name="validateValue">A delegate to be run when a value is set. This parameter is optional. Default is null.</param>
474         /// <param name="propertyChanged">A delegate to be run when the value has changed. This parameter is optional. Default is null.</param>
475         /// <param name="propertyChanging">A delegate to be run when the value will change. This parameter is optional. Default is null.</param>
476         /// <param name="coerceValue">A delegate used to coerce the range of a value. This parameter is optional. Default is null.</param>
477         /// <param name="defaultValueCreator">A Func used to initialize default value for reference types.</param>
478         /// <returns>A newly created BindablePropertyKey.</returns>
479         public static BindablePropertyKey CreateReadOnly(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode = BindingMode.OneWayToSource,
480                                                          ValidateValueDelegate validateValue = null, BindingPropertyChangedDelegate propertyChanged = null, BindingPropertyChangingDelegate propertyChanging = null,
481                                                          CoerceValueDelegate coerceValue = null, CreateDefaultValueDelegate defaultValueCreator = null)
482         {
483             return
484                 new BindablePropertyKey(new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue,
485                     isReadOnly: true, defaultValueCreator: defaultValueCreator));
486         }
487
488         [Obsolete("Create<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
489         internal static BindableProperty Create<TDeclarer, TPropertyType>(Expression<Func<TDeclarer, TPropertyType>> getter, TPropertyType defaultValue, BindingMode defaultBindingMode,
490                                                                           ValidateValueDelegate<TPropertyType> validateValue, BindingPropertyChangedDelegate<TPropertyType> propertyChanged, BindingPropertyChangingDelegate<TPropertyType> propertyChanging,
491                                                                           CoerceValueDelegate<TPropertyType> coerceValue, BindablePropertyBindingChanging bindingChanging, bool isReadOnly = false,
492                                                                           CreateDefaultValueDelegate<TDeclarer, TPropertyType> defaultValueCreator = null) where TDeclarer : BindableObject
493         {
494             if (getter == null)
495                 throw new ArgumentNullException(nameof(getter));
496
497             Expression expr = getter.Body;
498
499             var unary = expr as UnaryExpression;
500             if (unary != null)
501                 expr = unary.Operand;
502
503             var member = expr as MemberExpression;
504             if (member == null)
505                 throw new ArgumentException("getter must be a MemberExpression", nameof(getter));
506
507             var property = (PropertyInfo)member.Member;
508
509             ValidateValueDelegate untypedValidateValue = null;
510             BindingPropertyChangedDelegate untypedBindingPropertyChanged = null;
511             BindingPropertyChangingDelegate untypedBindingPropertyChanging = null;
512             CoerceValueDelegate untypedCoerceValue = null;
513             CreateDefaultValueDelegate untypedDefaultValueCreator = null;
514             if (validateValue != null)
515                 untypedValidateValue = (bindable, value) => validateValue(bindable, (TPropertyType)value);
516             if (propertyChanged != null)
517                 untypedBindingPropertyChanged = (bindable, oldValue, newValue) => propertyChanged(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
518             if (propertyChanging != null)
519                 untypedBindingPropertyChanging = (bindable, oldValue, newValue) => propertyChanging(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
520             if (coerceValue != null)
521                 untypedCoerceValue = (bindable, value) => coerceValue(bindable, (TPropertyType)value);
522             if (defaultValueCreator != null)
523                 untypedDefaultValueCreator = o => defaultValueCreator((TDeclarer)o);
524
525             return new BindableProperty(property.Name, property.PropertyType, typeof(TDeclarer), defaultValue, defaultBindingMode, untypedValidateValue, untypedBindingPropertyChanged,
526                 untypedBindingPropertyChanging, untypedCoerceValue, bindingChanging, isReadOnly, untypedDefaultValueCreator);
527         }
528
529         internal static BindableProperty Create(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
530                                                 BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
531                                                 CreateDefaultValueDelegate defaultValueCreator = null)
532         {
533             return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging,
534                 defaultValueCreator: defaultValueCreator);
535         }
536
537         [Obsolete("CreateAttached<> (generic) is obsolete as of version 2.1.0 and is no longer supported.")]
538         internal static BindableProperty CreateAttached<TDeclarer, TPropertyType>(Expression<Func<BindableObject, TPropertyType>> staticgetter, TPropertyType defaultValue, BindingMode defaultBindingMode,
539                                                                                   ValidateValueDelegate<TPropertyType> validateValue, BindingPropertyChangedDelegate<TPropertyType> propertyChanged, BindingPropertyChangingDelegate<TPropertyType> propertyChanging,
540                                                                                   CoerceValueDelegate<TPropertyType> coerceValue, BindablePropertyBindingChanging bindingChanging, bool isReadOnly = false,
541                                                                                   CreateDefaultValueDelegate<BindableObject, TPropertyType> defaultValueCreator = null)
542         {
543             if (staticgetter == null)
544                 throw new ArgumentNullException(nameof(staticgetter));
545
546             Expression expr = staticgetter.Body;
547
548             var unary = expr as UnaryExpression;
549             if (unary != null)
550                 expr = unary.Operand;
551
552             var methodcall = expr as MethodCallExpression;
553             if (methodcall == null)
554                 throw new ArgumentException("staticgetter must be a MethodCallExpression", nameof(staticgetter));
555
556             MethodInfo method = methodcall.Method;
557             if (!method.Name.StartsWith("Get", StringComparison.Ordinal))
558                 throw new ArgumentException("staticgetter name must start with Get", nameof(staticgetter));
559
560             string propertyname = method.Name.Substring(3);
561
562             ValidateValueDelegate untypedValidateValue = null;
563             BindingPropertyChangedDelegate untypedBindingPropertyChanged = null;
564             BindingPropertyChangingDelegate untypedBindingPropertyChanging = null;
565             CoerceValueDelegate untypedCoerceValue = null;
566             CreateDefaultValueDelegate untypedDefaultValueCreator = null;
567             if (validateValue != null)
568                 untypedValidateValue = (bindable, value) => validateValue(bindable, (TPropertyType)value);
569             if (propertyChanged != null)
570                 untypedBindingPropertyChanged = (bindable, oldValue, newValue) => propertyChanged(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
571             if (propertyChanging != null)
572                 untypedBindingPropertyChanging = (bindable, oldValue, newValue) => propertyChanging(bindable, (TPropertyType)oldValue, (TPropertyType)newValue);
573             if (coerceValue != null)
574                 untypedCoerceValue = (bindable, value) => coerceValue(bindable, (TPropertyType)value);
575             if (defaultValueCreator != null)
576                 untypedDefaultValueCreator = o => defaultValueCreator(o);
577
578             return new BindableProperty(propertyname, method.ReturnType, typeof(TDeclarer), defaultValue, defaultBindingMode, untypedValidateValue, untypedBindingPropertyChanged, untypedBindingPropertyChanging,
579                 untypedCoerceValue, bindingChanging, isReadOnly, untypedDefaultValueCreator);
580         }
581
582         internal static BindableProperty CreateAttached(string propertyName, Type returnType, Type declaringType, object defaultValue, BindingMode defaultBindingMode, ValidateValueDelegate validateValue,
583                                                         BindingPropertyChangedDelegate propertyChanged, BindingPropertyChangingDelegate propertyChanging, CoerceValueDelegate coerceValue, BindablePropertyBindingChanging bindingChanging,
584                                                         bool isReadOnly, CreateDefaultValueDelegate defaultValueCreator = null)
585         {
586             return new BindableProperty(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValue, propertyChanged, propertyChanging, coerceValue, bindingChanging, isReadOnly,
587                 defaultValueCreator);
588         }
589
590         internal object GetDefaultValue(BindableObject bindable)
591         {
592             if (DefaultValueCreator != null)
593                 return DefaultValueCreator(bindable);
594
595             return DefaultValue;
596         }
597
598         internal bool TryConvert(ref object value)
599         {
600             if (value == null)
601             {
602                 return !ReturnTypeInfo.IsValueType || ReturnTypeInfo.IsGenericType && ReturnTypeInfo.GetGenericTypeDefinition() == typeof(Nullable<>);
603             }
604
605             Type valueType = value.GetType();
606             Type type = ReturnType;
607
608             // Dont support arbitrary IConvertible by limiting which types can use this
609             Type[] convertableTo;
610             TypeConverter typeConverterTo;
611             if (SimpleConvertTypes.TryGetValue(valueType, out convertableTo) && Array.IndexOf(convertableTo, type) != -1)
612             {
613                 value = Convert.ChangeType(value, type);
614             }
615             else if (WellKnownConvertTypes.TryGetValue(type, out typeConverterTo) && typeConverterTo.CanConvertFrom(valueType))
616             {
617                 value = typeConverterTo.ConvertFromInvariantString(value.ToString());
618             }
619             else if (UserCustomConvertTypes.TryGetValue(type, out typeConverterTo) && typeConverterTo.CanConvertFrom(valueType))
620             {
621                 //Modification for NUI XAML : user defined converter for DynamicResource can be added
622                 value = typeConverterTo.ConvertFromInvariantString(value.ToString());
623             }
624             else if (!ReturnTypeInfo.IsAssignableFrom(valueType.GetTypeInfo()))
625             {
626                 var cast = type.GetImplicitConversionOperator(fromType: valueType, toType: type)
627                         ?? valueType.GetImplicitConversionOperator(fromType: valueType, toType: type);
628
629                 if (cast == null)
630                     return false;
631
632                 value = cast.Invoke(null, new[] { value });
633             }
634
635             return true;
636         }
637
638         internal delegate void BindablePropertyBindingChanging(BindableObject bindable, BindingBase oldValue, BindingBase newValue);
639     }
640 }