PerspexObject.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. // Copyright (c) The Perspex Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections.Generic;
  5. using System.ComponentModel;
  6. using System.Linq;
  7. using System.Reactive.Disposables;
  8. using System.Reactive.Linq;
  9. using System.Reflection;
  10. using Perspex.Reactive;
  11. using Perspex.Utilities;
  12. using Serilog;
  13. using Serilog.Core.Enrichers;
  14. namespace Perspex
  15. {
  16. /// <summary>
  17. /// The priority of a binding.
  18. /// </summary>
  19. public enum BindingPriority
  20. {
  21. /// <summary>
  22. /// A value that comes from an animation.
  23. /// </summary>
  24. Animation = -1,
  25. /// <summary>
  26. /// A local value.
  27. /// </summary>
  28. LocalValue = 0,
  29. /// <summary>
  30. /// A triggered style binding.
  31. /// </summary>
  32. /// <remarks>
  33. /// A style trigger is a selector such as .class which overrides a
  34. /// <see cref="TemplatedParent"/> binding. In this way, a basic control can have
  35. /// for example a Background from the templated parent which changes when the
  36. /// control has the :pointerover class.
  37. /// </remarks>
  38. StyleTrigger,
  39. /// <summary>
  40. /// A binding to a property on the templated parent.
  41. /// </summary>
  42. TemplatedParent,
  43. /// <summary>
  44. /// A style binding.
  45. /// </summary>
  46. Style,
  47. /// <summary>
  48. /// The binding is uninitialized.
  49. /// </summary>
  50. Unset = int.MaxValue,
  51. }
  52. /// <summary>
  53. /// An object with <see cref="PerspexProperty"/> support.
  54. /// </summary>
  55. /// <remarks>
  56. /// This class is analogous to DependencyObject in WPF.
  57. /// </remarks>
  58. public class PerspexObject : IObservablePropertyBag, INotifyPropertyChanged
  59. {
  60. /// <summary>
  61. /// The registered properties by type.
  62. /// </summary>
  63. private static readonly Dictionary<Type, List<PerspexProperty>> s_registered =
  64. new Dictionary<Type, List<PerspexProperty>>();
  65. /// <summary>
  66. /// The parent object that inherited values are inherited from.
  67. /// </summary>
  68. private PerspexObject _inheritanceParent;
  69. /// <summary>
  70. /// The set values/bindings on this object.
  71. /// </summary>
  72. private readonly Dictionary<PerspexProperty, PriorityValue> _values =
  73. new Dictionary<PerspexProperty, PriorityValue>();
  74. /// <summary>
  75. /// Event handler for <see cref="INotifyPropertyChanged"/> implementation.
  76. /// </summary>
  77. private PropertyChangedEventHandler _inpcChanged;
  78. /// <summary>
  79. /// A serilog logger for logging property events.
  80. /// </summary>
  81. private readonly ILogger _propertyLog;
  82. /// <summary>
  83. /// Initializes a new instance of the <see cref="PerspexObject"/> class.
  84. /// </summary>
  85. public PerspexObject()
  86. {
  87. _propertyLog = Log.ForContext(new[]
  88. {
  89. new PropertyEnricher("Area", "Property"),
  90. new PropertyEnricher("SourceContext", GetType()),
  91. new PropertyEnricher("Id", GetHashCode()),
  92. });
  93. foreach (var property in GetRegisteredProperties())
  94. {
  95. var e = new PerspexPropertyChangedEventArgs(
  96. this,
  97. property,
  98. PerspexProperty.UnsetValue,
  99. property.GetDefaultValue(GetType()),
  100. BindingPriority.Unset);
  101. property.NotifyInitialized(e);
  102. }
  103. }
  104. /// <summary>
  105. /// Raised when a <see cref="PerspexProperty"/> value changes on this object.
  106. /// </summary>
  107. public event EventHandler<PerspexPropertyChangedEventArgs> PropertyChanged;
  108. /// <summary>
  109. /// Raised when a <see cref="PerspexProperty"/> value changes on this object.
  110. /// </summary>
  111. event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
  112. {
  113. add { _inpcChanged += value; }
  114. remove { _inpcChanged -= value; }
  115. }
  116. /// <summary>
  117. /// Gets or sets the parent object that inherited <see cref="PerspexProperty"/> values
  118. /// are inherited from.
  119. /// </summary>
  120. /// <value>
  121. /// The inheritance parent.
  122. /// </value>
  123. protected PerspexObject InheritanceParent
  124. {
  125. get
  126. {
  127. return _inheritanceParent;
  128. }
  129. set
  130. {
  131. if (_inheritanceParent != value)
  132. {
  133. if (_inheritanceParent != null)
  134. {
  135. _inheritanceParent.PropertyChanged -= ParentPropertyChanged;
  136. }
  137. var inherited = (from property in GetProperties(GetType())
  138. where property.Inherits
  139. select new
  140. {
  141. Property = property,
  142. Value = GetValue(property),
  143. }).ToList();
  144. _inheritanceParent = value;
  145. foreach (var i in inherited)
  146. {
  147. object newValue = GetValue(i.Property);
  148. if (!Equals(i.Value, newValue))
  149. {
  150. RaisePropertyChanged(i.Property, i.Value, newValue, BindingPriority.LocalValue);
  151. }
  152. }
  153. if (_inheritanceParent != null)
  154. {
  155. _inheritanceParent.PropertyChanged += ParentPropertyChanged;
  156. }
  157. }
  158. }
  159. }
  160. /// <summary>
  161. /// Gets or sets the value of a <see cref="PerspexProperty"/>.
  162. /// </summary>
  163. /// <param name="property">The property.</param>
  164. public object this[PerspexProperty property]
  165. {
  166. get { return GetValue(property); }
  167. set { SetValue(property, value); }
  168. }
  169. /// <summary>
  170. /// Gets or sets a binding for a <see cref="PerspexProperty"/>.
  171. /// </summary>
  172. /// <param name="binding">The binding information.</param>
  173. public IObservable<object> this[BindingDescriptor binding]
  174. {
  175. get
  176. {
  177. return new BindingDescriptor
  178. {
  179. Mode = binding.Mode,
  180. Priority = binding.Priority,
  181. Property = binding.Property,
  182. Source = this,
  183. };
  184. }
  185. set
  186. {
  187. var mode = (binding.Mode == BindingMode.Default) ?
  188. binding.Property.DefaultBindingMode :
  189. binding.Mode;
  190. var sourceBinding = value as BindingDescriptor;
  191. if (sourceBinding == null && mode != BindingMode.OneWay)
  192. {
  193. throw new InvalidOperationException("Can only bind OneWay to plain IObservable.");
  194. }
  195. switch (mode)
  196. {
  197. case BindingMode.Default:
  198. case BindingMode.OneWay:
  199. Bind(binding.Property, value, binding.Priority);
  200. break;
  201. case BindingMode.OneTime:
  202. SetValue(binding.Property, sourceBinding.Source.GetValue(sourceBinding.Property), binding.Priority);
  203. break;
  204. case BindingMode.OneWayToSource:
  205. sourceBinding.Source.Bind(sourceBinding.Property, GetObservable(binding.Property), binding.Priority);
  206. break;
  207. case BindingMode.TwoWay:
  208. BindTwoWay(binding.Property, sourceBinding.Source, sourceBinding.Property);
  209. break;
  210. }
  211. }
  212. }
  213. /// <summary>
  214. /// Gets all <see cref="PerspexProperty"/>s registered on a type.
  215. /// </summary>
  216. /// <param name="type">The type.</param>
  217. /// <returns>A collection of <see cref="PerspexProperty"/> definitions.</returns>
  218. public static IEnumerable<PerspexProperty> GetProperties(Type type)
  219. {
  220. Contract.Requires<NullReferenceException>(type != null);
  221. TypeInfo i = type.GetTypeInfo();
  222. while (type != null)
  223. {
  224. List<PerspexProperty> list;
  225. if (s_registered.TryGetValue(type, out list))
  226. {
  227. foreach (PerspexProperty p in list)
  228. {
  229. yield return p;
  230. }
  231. }
  232. type = type.GetTypeInfo().BaseType;
  233. }
  234. }
  235. /// <summary>
  236. /// Registers a <see cref="PerspexProperty"/> on a type.
  237. /// </summary>
  238. /// <param name="type">The type.</param>
  239. /// <param name="property">The property.</param>
  240. /// <remarks>
  241. /// You won't usually want to call this method directly, instead use the
  242. /// <see cref="PerspexProperty.Register"/> method.
  243. /// </remarks>
  244. public static void Register(Type type, PerspexProperty property)
  245. {
  246. Contract.Requires<NullReferenceException>(type != null);
  247. Contract.Requires<NullReferenceException>(property != null);
  248. List<PerspexProperty> list;
  249. if (!s_registered.TryGetValue(type, out list))
  250. {
  251. list = new List<PerspexProperty>();
  252. s_registered.Add(type, list);
  253. }
  254. if (!list.Contains(property))
  255. {
  256. list.Add(property);
  257. }
  258. }
  259. /// <summary>
  260. /// Clears a <see cref="PerspexProperty"/>'s local value.
  261. /// </summary>
  262. /// <param name="property">The property.</param>
  263. public void ClearValue(PerspexProperty property)
  264. {
  265. Contract.Requires<NullReferenceException>(property != null);
  266. SetValue(property, PerspexProperty.UnsetValue);
  267. }
  268. /// <summary>
  269. /// Gets an observable for a <see cref="PerspexProperty"/>.
  270. /// </summary>
  271. /// <param name="property">The property.</param>
  272. /// <returns>An observable.</returns>
  273. public IObservable<object> GetObservable(PerspexProperty property)
  274. {
  275. Contract.Requires<NullReferenceException>(property != null);
  276. return new PerspexObservable<object>(
  277. observer =>
  278. {
  279. EventHandler<PerspexPropertyChangedEventArgs> handler = (s, e) =>
  280. {
  281. if (e.Property == property)
  282. {
  283. observer.OnNext(e.NewValue);
  284. }
  285. };
  286. observer.OnNext(GetValue(property));
  287. PropertyChanged += handler;
  288. return Disposable.Create(() =>
  289. {
  290. PropertyChanged -= handler;
  291. });
  292. },
  293. GetObservableDescription(property));
  294. }
  295. /// <summary>
  296. /// Gets an observable for a <see cref="PerspexProperty"/>.
  297. /// </summary>
  298. /// <typeparam name="T">The property type.</typeparam>
  299. /// <param name="property">The property.</param>
  300. /// <returns>An observable.</returns>
  301. public IObservable<T> GetObservable<T>(PerspexProperty<T> property)
  302. {
  303. Contract.Requires<NullReferenceException>(property != null);
  304. return GetObservable((PerspexProperty)property).Cast<T>();
  305. }
  306. /// <summary>
  307. /// Gets an observable for a <see cref="PerspexProperty"/>.
  308. /// </summary>
  309. /// <typeparam name="T">The type of the property.</typeparam>
  310. /// <param name="property">The property.</param>
  311. /// <returns>An observable which when subscribed pushes the old and new values of the
  312. /// property each time it is changed.</returns>
  313. public IObservable<Tuple<T, T>> GetObservableWithHistory<T>(PerspexProperty<T> property)
  314. {
  315. return new PerspexObservable<Tuple<T, T>>(
  316. observer =>
  317. {
  318. EventHandler<PerspexPropertyChangedEventArgs> handler = (s, e) =>
  319. {
  320. if (e.Property == property)
  321. {
  322. observer.OnNext(Tuple.Create((T)e.OldValue, (T)e.NewValue));
  323. }
  324. };
  325. PropertyChanged += handler;
  326. return Disposable.Create(() =>
  327. {
  328. PropertyChanged -= handler;
  329. });
  330. },
  331. GetObservableDescription(property));
  332. }
  333. /// <summary>
  334. /// Gets a <see cref="PerspexProperty"/> value.
  335. /// </summary>
  336. /// <param name="property">The property.</param>
  337. /// <returns>The value.</returns>
  338. public object GetValue(PerspexProperty property)
  339. {
  340. Contract.Requires<NullReferenceException>(property != null);
  341. object result;
  342. PriorityValue value;
  343. if (_values.TryGetValue(property, out value))
  344. {
  345. result = value.Value;
  346. }
  347. else
  348. {
  349. result = PerspexProperty.UnsetValue;
  350. }
  351. if (result == PerspexProperty.UnsetValue)
  352. {
  353. result = GetDefaultValue(property);
  354. }
  355. return result;
  356. }
  357. /// <summary>
  358. /// Gets a <see cref="PerspexProperty"/> value.
  359. /// </summary>
  360. /// <typeparam name="T">The type of the property.</typeparam>
  361. /// <param name="property">The property.</param>
  362. /// <returns>The value.</returns>
  363. public T GetValue<T>(PerspexProperty<T> property)
  364. {
  365. Contract.Requires<NullReferenceException>(property != null);
  366. return (T)GetValue((PerspexProperty)property);
  367. }
  368. /// <summary>
  369. /// Gets all properties that are registered on this object.
  370. /// </summary>
  371. /// <returns>
  372. /// A collection of <see cref="PerspexProperty"/> objects.
  373. /// </returns>
  374. public IEnumerable<PerspexProperty> GetRegisteredProperties()
  375. {
  376. Type type = GetType();
  377. while (type != null)
  378. {
  379. List<PerspexProperty> list;
  380. if (s_registered.TryGetValue(type, out list))
  381. {
  382. foreach (var p in list)
  383. {
  384. yield return p;
  385. }
  386. }
  387. type = type.GetTypeInfo().BaseType;
  388. }
  389. }
  390. /// <summary>
  391. /// Checks whether a <see cref="PerspexProperty"/> is set on this object.
  392. /// </summary>
  393. /// <param name="property">The property.</param>
  394. /// <returns>True if the property is set, otherwise false.</returns>
  395. public bool IsSet(PerspexProperty property)
  396. {
  397. Contract.Requires<NullReferenceException>(property != null);
  398. return _values.ContainsKey(property);
  399. }
  400. /// <summary>
  401. /// Checks whether a <see cref="PerspexProperty"/> is registered on this class.
  402. /// </summary>
  403. /// <param name="property">The property.</param>
  404. /// <returns>True if the property is registered, otherwise false.</returns>
  405. public bool IsRegistered(PerspexProperty property)
  406. {
  407. Type type = GetType();
  408. while (type != null)
  409. {
  410. List<PerspexProperty> list;
  411. if (s_registered.TryGetValue(type, out list))
  412. {
  413. if (list.Contains(property))
  414. {
  415. return true;
  416. }
  417. }
  418. type = type.GetTypeInfo().BaseType;
  419. }
  420. return false;
  421. }
  422. /// <summary>
  423. /// Sets a <see cref="PerspexProperty"/> value.
  424. /// </summary>
  425. /// <param name="property">The property.</param>
  426. /// <param name="value">The value.</param>
  427. /// <param name="priority">The priority of the value.</param>
  428. public void SetValue(
  429. PerspexProperty property,
  430. object value,
  431. BindingPriority priority = BindingPriority.LocalValue)
  432. {
  433. Contract.Requires<NullReferenceException>(property != null);
  434. PriorityValue v;
  435. if (!IsRegistered(property))
  436. {
  437. throw new InvalidOperationException(string.Format(
  438. "Property '{0}' not registered on '{1}'",
  439. property.Name,
  440. GetType()));
  441. }
  442. if (!TypeUtilities.TryCast(property.PropertyType, value, out value))
  443. {
  444. throw new InvalidOperationException(string.Format(
  445. "Invalid value for Property '{0}': {1} ({2})",
  446. property.Name,
  447. value,
  448. value?.GetType().FullName ?? "(null)"));
  449. }
  450. if (!_values.TryGetValue(property, out v))
  451. {
  452. if (value == PerspexProperty.UnsetValue)
  453. {
  454. return;
  455. }
  456. v = CreatePriorityValue(property);
  457. _values.Add(property, v);
  458. }
  459. _propertyLog.Verbose(
  460. "Set {Property} to {$Value} with priority {Priority}",
  461. property,
  462. value,
  463. priority);
  464. v.SetDirectValue(value, (int)priority);
  465. }
  466. /// <summary>
  467. /// Sets a <see cref="PerspexProperty"/> value.
  468. /// </summary>
  469. /// <typeparam name="T">The type of the property.</typeparam>
  470. /// <param name="property">The property.</param>
  471. /// <param name="value">The value.</param>
  472. /// <param name="priority">The priority of the value.</param>
  473. public void SetValue<T>(
  474. PerspexProperty<T> property,
  475. T value,
  476. BindingPriority priority = BindingPriority.LocalValue)
  477. {
  478. Contract.Requires<NullReferenceException>(property != null);
  479. SetValue((PerspexProperty)property, value, priority);
  480. }
  481. /// <summary>
  482. /// Binds a <see cref="PerspexProperty"/> to an observable.
  483. /// </summary>
  484. /// <param name="property">The property.</param>
  485. /// <param name="source">The observable.</param>
  486. /// <param name="priority">The priority of the binding.</param>
  487. /// <returns>
  488. /// A disposable which can be used to terminate the binding.
  489. /// </returns>
  490. public IDisposable Bind(
  491. PerspexProperty property,
  492. IObservable<object> source,
  493. BindingPriority priority = BindingPriority.LocalValue)
  494. {
  495. Contract.Requires<NullReferenceException>(property != null);
  496. PriorityValue v;
  497. IDescription description = source as IDescription;
  498. if (!IsRegistered(property))
  499. {
  500. throw new InvalidOperationException(string.Format(
  501. "Property '{0}' not registered on '{1}'",
  502. property.Name,
  503. GetType()));
  504. }
  505. if (!_values.TryGetValue(property, out v))
  506. {
  507. v = CreatePriorityValue(property);
  508. _values.Add(property, v);
  509. }
  510. _propertyLog.Verbose(
  511. "Bound {Property} to {Binding} with priority {Priority}",
  512. property,
  513. source,
  514. priority);
  515. return v.Add(source, (int)priority);
  516. }
  517. /// <summary>
  518. /// Binds a <see cref="PerspexProperty"/> to an observable.
  519. /// </summary>
  520. /// <typeparam name="T">The type of the property.</typeparam>
  521. /// <param name="property">The property.</param>
  522. /// <param name="source">The observable.</param>
  523. /// <param name="priority">The priority of the binding.</param>
  524. /// <returns>
  525. /// A disposable which can be used to terminate the binding.
  526. /// </returns>
  527. public IDisposable Bind<T>(
  528. PerspexProperty<T> property,
  529. IObservable<T> source,
  530. BindingPriority priority = BindingPriority.LocalValue)
  531. {
  532. Contract.Requires<NullReferenceException>(property != null);
  533. return Bind((PerspexProperty)property, source.Select(x => (object)x), priority);
  534. }
  535. /// <summary>
  536. /// Initialites a two-way bind between <see cref="PerspexProperty"/>s.
  537. /// </summary>
  538. /// <param name="property">The property on this object.</param>
  539. /// <param name="source">The source object.</param>
  540. /// <param name="sourceProperty">The property on the source object.</param>
  541. /// <param name="priority">The priority of the binding.</param>
  542. /// <returns>
  543. /// A disposable which can be used to terminate the binding.
  544. /// </returns>
  545. /// <remarks>
  546. /// The binding is first carried out from <paramref name="source"/> to this.
  547. /// </remarks>
  548. public IDisposable BindTwoWay(
  549. PerspexProperty property,
  550. PerspexObject source,
  551. PerspexProperty sourceProperty,
  552. BindingPriority priority = BindingPriority.LocalValue)
  553. {
  554. return new CompositeDisposable(
  555. Bind(property, source.GetObservable(sourceProperty)),
  556. source.Bind(sourceProperty, GetObservable(property)));
  557. }
  558. /// <summary>
  559. /// Forces the specified property to be revalidated.
  560. /// </summary>
  561. /// <param name="property">The property.</param>
  562. public void Revalidate(PerspexProperty property)
  563. {
  564. PriorityValue value;
  565. if (_values.TryGetValue(property, out value))
  566. {
  567. value.Revalidate();
  568. }
  569. }
  570. /// <summary>
  571. /// Gets all priority values set on the object.
  572. /// </summary>
  573. /// <returns>A collection of property/value tuples.</returns>
  574. internal IDictionary<PerspexProperty, PriorityValue> GetSetValues()
  575. {
  576. return _values;
  577. }
  578. /// <summary>
  579. /// Forces revalidation of properties when a property value changes.
  580. /// </summary>
  581. /// <param name="property">The property to that affects validation.</param>
  582. /// <param name="affected">The affected properties.</param>
  583. protected static void AffectsValidation(PerspexProperty property, params PerspexProperty[] affected)
  584. {
  585. property.Changed.Subscribe(e =>
  586. {
  587. foreach (var p in affected)
  588. {
  589. e.Sender.Revalidate(p);
  590. }
  591. });
  592. }
  593. /// <summary>
  594. /// Called when a perspex property changes on the object.
  595. /// </summary>
  596. /// <param name="e">The event arguments.</param>
  597. protected virtual void OnPropertyChanged(PerspexPropertyChangedEventArgs e)
  598. {
  599. }
  600. /// <summary>
  601. /// Creates a <see cref="PriorityValue"/> for a <see cref="PerspexProperty"/>.
  602. /// </summary>
  603. /// <param name="property">The property.</param>
  604. /// <returns>The <see cref="PriorityValue"/>.</returns>
  605. private PriorityValue CreatePriorityValue(PerspexProperty property)
  606. {
  607. Func<PerspexObject, object, object> validate = property.GetValidationFunc(GetType());
  608. Func<object, object> validate2 = null;
  609. if (validate != null)
  610. {
  611. validate2 = v => validate(this, v);
  612. }
  613. PriorityValue result = new PriorityValue(property.Name, property.PropertyType, validate2);
  614. result.Changed.Subscribe(x =>
  615. {
  616. object oldValue = (x.Item1 == PerspexProperty.UnsetValue) ?
  617. GetDefaultValue(property) :
  618. x.Item1;
  619. object newValue = (x.Item2 == PerspexProperty.UnsetValue) ?
  620. GetDefaultValue(property) :
  621. x.Item2;
  622. if (!Equals(oldValue, newValue))
  623. {
  624. RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)result.ValuePriority);
  625. _propertyLog.Verbose(
  626. "{Property} changed from {$Old} to {$Value} with priority {Priority}",
  627. property,
  628. oldValue,
  629. newValue,
  630. (BindingPriority)result.ValuePriority);
  631. }
  632. });
  633. return result;
  634. }
  635. /// <summary>
  636. /// Gets the default value for a property.
  637. /// </summary>
  638. /// <param name="property">The property.</param>
  639. /// <returns>The default value.</returns>
  640. private object GetDefaultValue(PerspexProperty property)
  641. {
  642. if (property.Inherits && _inheritanceParent != null)
  643. {
  644. return _inheritanceParent.GetValue(property);
  645. }
  646. else
  647. {
  648. return property.GetDefaultValue(GetType());
  649. }
  650. }
  651. /// <summary>
  652. /// Called when a property is changed on the current <see cref="InheritanceParent"/>.
  653. /// </summary>
  654. /// <param name="sender">The event sender.</param>
  655. /// <param name="e">The event args.</param>
  656. /// <remarks>
  657. /// Checks for changes in an inherited property value.
  658. /// </remarks>
  659. private void ParentPropertyChanged(object sender, PerspexPropertyChangedEventArgs e)
  660. {
  661. Contract.Requires<ArgumentNullException>(e != null);
  662. if (e.Property.Inherits && !IsSet(e.Property))
  663. {
  664. RaisePropertyChanged(e.Property, e.OldValue, e.NewValue, BindingPriority.LocalValue);
  665. }
  666. }
  667. /// <summary>
  668. /// Gets a description of a property that van be used in observables.
  669. /// </summary>
  670. /// <param name="property">The property</param>
  671. /// <returns>The description.</returns>
  672. private string GetObservableDescription(PerspexProperty property)
  673. {
  674. return string.Format("{0}.{1}", GetType().Name, property.Name);
  675. }
  676. /// <summary>
  677. /// Raises the <see cref="PropertyChanged"/> event.
  678. /// </summary>
  679. /// <param name="property">The property that has changed.</param>
  680. /// <param name="oldValue">The old property value.</param>
  681. /// <param name="newValue">The new property value.</param>
  682. /// <param name="priority">The priority of the binding that produced the value.</param>
  683. private void RaisePropertyChanged(
  684. PerspexProperty property,
  685. object oldValue,
  686. object newValue,
  687. BindingPriority priority)
  688. {
  689. Contract.Requires<NullReferenceException>(property != null);
  690. PerspexPropertyChangedEventArgs e = new PerspexPropertyChangedEventArgs(
  691. this,
  692. property,
  693. oldValue,
  694. newValue,
  695. priority);
  696. OnPropertyChanged(e);
  697. property.NotifyChanged(e);
  698. if (PropertyChanged != null)
  699. {
  700. PropertyChanged(this, e);
  701. }
  702. if (_inpcChanged != null)
  703. {
  704. PropertyChangedEventArgs e2 = new PropertyChangedEventArgs(property.Name);
  705. _inpcChanged(this, e2);
  706. }
  707. }
  708. }
  709. }