BindingOperations.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // Copyright (c) The Avalonia 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.Reactive.Disposables;
  5. using System.Reactive.Linq;
  6. using Avalonia.Reactive;
  7. namespace Avalonia.Data
  8. {
  9. public static class BindingOperations
  10. {
  11. public static readonly object DoNothing = new DoNothingType();
  12. /// <summary>
  13. /// Applies an <see cref="InstancedBinding"/> a property on an <see cref="IAvaloniaObject"/>.
  14. /// </summary>
  15. /// <param name="target">The target object.</param>
  16. /// <param name="property">The property to bind.</param>
  17. /// <param name="binding">The instanced binding.</param>
  18. /// <param name="anchor">
  19. /// An optional anchor from which to locate required context. When binding to objects that
  20. /// are not in the logical tree, certain types of binding need an anchor into the tree in
  21. /// order to locate named controls or resources. The <paramref name="anchor"/> parameter
  22. /// can be used to provide this context.
  23. /// </param>
  24. /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
  25. public static IDisposable Apply(
  26. IAvaloniaObject target,
  27. AvaloniaProperty property,
  28. InstancedBinding binding,
  29. object anchor)
  30. {
  31. Contract.Requires<ArgumentNullException>(target != null);
  32. Contract.Requires<ArgumentNullException>(property != null);
  33. Contract.Requires<ArgumentNullException>(binding != null);
  34. var mode = binding.Mode;
  35. if (mode == BindingMode.Default)
  36. {
  37. mode = property.GetMetadata(target.GetType()).DefaultBindingMode;
  38. }
  39. switch (mode)
  40. {
  41. case BindingMode.Default:
  42. case BindingMode.OneWay:
  43. return target.Bind(property, binding.Observable ?? binding.Subject, binding.Priority);
  44. case BindingMode.TwoWay:
  45. return new CompositeDisposable(
  46. target.Bind(property, binding.Subject, binding.Priority),
  47. target.GetObservable(property).Subscribe(binding.Subject));
  48. case BindingMode.OneTime:
  49. var source = binding.Subject ?? binding.Observable;
  50. if (source != null)
  51. {
  52. // Perf: Avoid allocating closure in the outer scope.
  53. var targetCopy = target;
  54. var propertyCopy = property;
  55. var bindingCopy = binding;
  56. return source
  57. .Where(x => BindingNotification.ExtractValue(x) != AvaloniaProperty.UnsetValue)
  58. .Take(1)
  59. .Subscribe(x => targetCopy.SetValue(
  60. propertyCopy,
  61. BindingNotification.ExtractValue(x),
  62. bindingCopy.Priority));
  63. }
  64. else
  65. {
  66. target.SetValue(property, binding.Value, binding.Priority);
  67. return Disposable.Empty;
  68. }
  69. case BindingMode.OneWayToSource:
  70. {
  71. // Perf: Avoid allocating closure in the outer scope.
  72. var bindingCopy = binding;
  73. return Observable.CombineLatest(
  74. binding.Observable,
  75. target.GetObservable(property),
  76. (_, v) => v)
  77. .Subscribe(x => bindingCopy.Subject.OnNext(x));
  78. }
  79. default:
  80. throw new ArgumentException("Invalid binding mode.");
  81. }
  82. }
  83. }
  84. public sealed class DoNothingType
  85. {
  86. internal DoNothingType() { }
  87. /// <summary>
  88. /// Returns the string representation of <see cref="BindingOperations.DoNothing"/>.
  89. /// </summary>
  90. /// <returns>The string "(do nothing)".</returns>
  91. public override string ToString() => "(do nothing)";
  92. }
  93. }