diff --git a/Softeq.XToolkit.Bindings.Droid/DroidBinding.cs b/Softeq.XToolkit.Bindings.Droid/DroidBinding.cs
index d80728a16..8b6edbdf0 100644
--- a/Softeq.XToolkit.Bindings.Droid/DroidBinding.cs
+++ b/Softeq.XToolkit.Bindings.Droid/DroidBinding.cs
@@ -8,8 +8,6 @@
using Android.Views;
using Android.Widget;
-#nullable disable
-
namespace Softeq.XToolkit.Bindings.Droid
{
///
@@ -19,11 +17,11 @@ public class DroidBinding : Binding
public DroidBinding(
object source,
string sourcePropertyName,
- object target = null,
- string targetPropertyName = null,
+ object? target = null,
+ string? targetPropertyName = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
: base(source, sourcePropertyName, target, targetPropertyName, mode, fallbackValue, targetNullValue)
{
}
@@ -32,11 +30,11 @@ public DroidBinding(
public DroidBinding(
object source,
Expression> sourcePropertyExpression,
- object target = null,
- Expression> targetPropertyExpression = null,
+ object? target = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
: base(source, sourcePropertyExpression, target, targetPropertyExpression, mode, fallbackValue, targetNullValue)
{
}
@@ -46,11 +44,11 @@ public DroidBinding(
object source,
Expression> sourcePropertyExpression,
bool? resolveTopField,
- object target = null,
- Expression> targetPropertyExpression = null,
+ object? target = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
: base(
source,
sourcePropertyExpression,
@@ -64,20 +62,23 @@ public DroidBinding(
}
///
- /// Define when the binding should be evaluated when the bound source object
- /// is a control. Because Xamarin controls are not DependencyObjects, the
- /// bound property will not automatically update the binding attached to it. Instead,
- /// use this method to define which of the control's events should be observed.
+ /// Define when the binding should be evaluated when the bound source object is a control.
+ ///
+ /// Because Xamarin controls are not DependencyObjects,
+ /// the bound property will not automatically update the binding attached to it.
+ ///
+ /// Instead, use this method to define which of the control's events should be observed.
///
///
- /// Defines the binding's update mode. Use
- /// to update the binding when
- /// the source control loses the focus. You can also use
- /// to update the binding
+ /// Defines the binding's update mode.
+ ///
+ /// Use to update the binding when the source control loses the focus.
+ /// You can also use to update the binding
/// when the source control's property changes.
+ ///
/// The PropertyChanged mode should only be used with the following items:
- /// - an EditText control and its Text property (TextChanged event).
- /// - a CompoundButton control and its Checked property (CheckedChange event).
+ /// - control and its Text property (TextChanged event).
+ /// - control and its Checked property (CheckedChange event).
///
/// The Binding instance.
///
@@ -87,60 +88,55 @@ public DroidBinding(
///
public Binding ObserveSourceEvent(UpdateTriggerMode mode = UpdateTriggerMode.PropertyChanged)
{
- switch (mode)
+ return mode switch
{
- case UpdateTriggerMode.LostFocus:
- return ObserveSourceEvent("FocusChange");
-
- case UpdateTriggerMode.PropertyChanged:
- return CheckControlSource();
- }
-
- return this;
+ UpdateTriggerMode.LostFocus => ObserveSourceEvent("FocusChange"),
+ UpdateTriggerMode.PropertyChanged => CheckControlSource(),
+ _ => this
+ };
}
///
- /// Define when the binding should be evaluated when the bound target object
- /// is a control. Because Xamarin controls are not DependencyObjects, the
- /// bound property will not automatically update the binding attached to it. Instead,
- /// use this method to define which of the control's events should be observed.
+ /// Define when the binding should be evaluated when the bound target object is a control.
+ ///
+ /// Because Xamarin controls are not DependencyObjects,
+ /// the bound property will not automatically update the binding attached to it.
+ ///
+ /// Instead, use this method to define which of the control's events should be observed.
///
///
- /// Defines the binding's update mode. Use
- /// to update the binding when
- /// the source control loses the focus. You can also use
- /// to update the binding
- /// when the source control's property changes.
+ /// Defines the binding's update mode.
+ ///
+ /// Use to update the binding when the target control loses the focus.
+ /// You can also use to update the binding
+ /// when the target control's property changes.
+ ///
/// The PropertyChanged mode should only be used with the following items:
- /// - an EditText control and its Text property (TextChanged event).
- /// - a CompoundButton control and its Checked property (CheckedChange event).
+ /// - control and its Text property (TextChanged event).
+ /// - control and its Checked property (CheckedChange event).
///
/// The Binding instance.
///
- /// When this method is called
- /// on a OneTime or a OneWay binding. This exception can
- /// also be thrown when the source object is null or has already been
- /// garbage collected before this method is called.
+ /// When this method is called on a OneTime or a OneWay binding.
+ /// This exception can also be thrown when the source object is null or
+ /// has already been garbage collected before this method is called.
///
public Binding ObserveTargetEvent(UpdateTriggerMode mode = UpdateTriggerMode.PropertyChanged)
{
- switch (mode)
+ return mode switch
{
- case UpdateTriggerMode.LostFocus:
- return ObserveTargetEvent("FocusChange");
-
- case UpdateTriggerMode.PropertyChanged:
- return CheckControlTarget();
- }
-
- return this;
+ UpdateTriggerMode.LostFocus => ObserveTargetEvent("FocusChange"),
+ UpdateTriggerMode.PropertyChanged => CheckControlTarget(),
+ _ => this
+ };
}
+ ///
[SuppressMessage("ReSharper", "RedundantAssignment", Justification = "Reviewed.")]
[SuppressMessage("ReSharper", "EntityNameCapturedOnly.Local", Justification = "Reviewed.")]
protected override Binding CheckControlSource()
{
- switch (PropertySource.Target)
+ switch (_propertySource!.Target)
{
case EditText textBox:
{
@@ -161,6 +157,7 @@ protected override Binding CheckControlSource()
}
}
+ ///
[SuppressMessage("ReSharper", "RedundantAssignment", Justification = "Reviewed.")]
[SuppressMessage("ReSharper", "EntityNameCapturedOnly.Local", Justification = "Reviewed.")]
protected override Binding CheckControlTarget()
@@ -170,7 +167,7 @@ protected override Binding CheckControlTarget()
return this;
}
- switch (PropertyTarget.Target)
+ switch (_propertyTarget!.Target)
{
case EditText textBox:
{
diff --git a/Softeq.XToolkit.Bindings.Droid/DroidBindingFactory.cs b/Softeq.XToolkit.Bindings.Droid/DroidBindingFactory.cs
index a1b645ef6..2459e3fa0 100644
--- a/Softeq.XToolkit.Bindings.Droid/DroidBindingFactory.cs
+++ b/Softeq.XToolkit.Bindings.Droid/DroidBindingFactory.cs
@@ -2,6 +2,7 @@
// http://www.softeq.com
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using System.Windows.Input;
@@ -9,10 +10,11 @@
using Android.Widget;
using Softeq.XToolkit.Common.Threading;
-#nullable disable
-
namespace Softeq.XToolkit.Bindings.Droid
{
+ ///
+ /// The Android-specific factory to create bindings.
+ ///
public class DroidBindingFactory : BindingFactoryBase
{
///
@@ -20,11 +22,11 @@ public override Binding CreateBinding(
object source,
Expression> sourcePropertyExpression,
bool? resolveTopField,
- object target = null,
- Expression> targetPropertyExpression = null,
+ object? target = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
return new DroidBinding(
source,
@@ -41,11 +43,11 @@ public override Binding CreateBinding(
public override Binding CreateBinding(
object source,
Expression> sourcePropertyExpression,
- object target = null,
- Expression> targetPropertyExpression = null,
+ object? target = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
return new DroidBinding(
source,
@@ -61,11 +63,11 @@ public override Binding CreateBinding(
public override Binding CreateBinding(
object source,
string sourcePropertyName,
- object target = null,
- string targetPropertyName = null,
+ object? target = null,
+ string? targetPropertyName = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
return new DroidBinding(
source,
@@ -77,12 +79,13 @@ public override Binding CreateBinding(
targetNullValue);
}
+ ///
public override Delegate GetCommandHandler(
EventInfo info,
string eventName,
Type elementType,
ICommand command,
- object commandParameter = null)
+ object? commandParameter = null)
{
if (string.IsNullOrEmpty(eventName) && elementType == typeof(CheckBox))
{
@@ -98,18 +101,19 @@ public override Delegate GetCommandHandler(
return base.GetCommandHandler(info, eventName, elementType, command, commandParameter);
}
+ ///
public override Delegate GetCommandHandler(
EventInfo info,
string eventName,
Type elementType,
ICommand command,
- Binding castedBinding)
+ Binding? castedBinding)
{
if (string.IsNullOrEmpty(eventName) && elementType == typeof(CheckBox))
{
return new EventHandler((s, args) =>
{
- object param = castedBinding == null ? default : castedBinding.Value;
+ var param = castedBinding == null ? default : castedBinding.Value;
if (command.CanExecute(param))
{
@@ -121,7 +125,8 @@ public override Delegate GetCommandHandler(
return base.GetCommandHandler(info, eventName, elementType, command, castedBinding);
}
- public override string GetDefaultEventNameForControl(Type type)
+ ///
+ public override string? GetDefaultEventNameForControl(Type type)
{
if (type == typeof(CheckBox) || typeof(CheckBox).IsAssignableFrom(type))
{
@@ -141,10 +146,11 @@ public override string GetDefaultEventNameForControl(Type type)
return null;
}
+ ///
public override void HandleCommandCanExecute(
object element,
ICommand command,
- Binding commandParameterBinding)
+ Binding? commandParameterBinding)
{
if (element is View view)
{
@@ -155,7 +161,7 @@ public override void HandleCommandCanExecute(
private static void HandleViewEnabled(
View view,
ICommand command,
- Binding commandParameterBinding)
+ Binding? commandParameterBinding)
{
var commandParameter = commandParameterBinding == null
? default
diff --git a/Softeq.XToolkit.Bindings.iOS/AppleBinding.cs b/Softeq.XToolkit.Bindings.iOS/AppleBinding.cs
index a02c624d8..fe6a46494 100644
--- a/Softeq.XToolkit.Bindings.iOS/AppleBinding.cs
+++ b/Softeq.XToolkit.Bindings.iOS/AppleBinding.cs
@@ -6,8 +6,6 @@
using System.Linq.Expressions;
using UIKit;
-#nullable disable
-
namespace Softeq.XToolkit.Bindings.iOS
{
///
@@ -17,11 +15,11 @@ public class AppleBinding : Binding
public AppleBinding(
object source,
string sourcePropertyName,
- object target = null,
- string targetPropertyName = null,
+ object? target = null,
+ string? targetPropertyName = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
: base(
source,
sourcePropertyName,
@@ -37,11 +35,11 @@ public AppleBinding(
public AppleBinding(
object source,
Expression> sourcePropertyExpression,
- object target = null,
- Expression> targetPropertyExpression = null,
+ object? target = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
: base(
source,
sourcePropertyExpression,
@@ -58,11 +56,11 @@ public AppleBinding(
object source,
Expression> sourcePropertyExpression,
bool? resolveTopField,
- object target = null,
- Expression> targetPropertyExpression = null,
+ object? target = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
: base(
source,
sourcePropertyExpression,
@@ -75,43 +73,90 @@ public AppleBinding(
{
}
+ ///
+ /// Define when the binding should be evaluated when the bound source object is a control.
+ ///
+ /// Because Xamarin controls are not DependencyObjects,
+ /// the bound property will not automatically update the binding attached to it.
+ ///
+ /// Instead, use this method to define which of the control's events should be observed.
+ ///
+ ///
+ /// Defines the binding's update mode.
+ ///
+ /// Use to update the binding when the source control's property changes.
+ ///
+ /// The PropertyChanged mode should only be used with the following items:
+ /// - control and its Text property (TextChanged event).
+ /// - control and its Text property (EditingChanged event).
+ /// - control and its On property (ValueChanged event).
+ ///
+ /// The Binding instance.
+ ///
+ /// When this method is called on a OneTime binding. Such bindings cannot be updated.
+ /// This exception can also be thrown when the source object is null or has already been
+ /// garbage collected before this method is called.
+ ///
+ ///
+ /// When is ,
+ /// because it only supported in Android at this time.
+ ///
public Binding ObserveSourceEvent(UpdateTriggerMode mode = UpdateTriggerMode.PropertyChanged)
{
- switch (mode)
+ return mode switch
{
- case UpdateTriggerMode.LostFocus:
- throw new ArgumentException(
- "UpdateTriggerMode.LostFocus is only supported in Android at this time",
- nameof(mode));
-
- case UpdateTriggerMode.PropertyChanged:
- return CheckControlSource();
- }
-
- return this;
+ UpdateTriggerMode.LostFocus => throw new ArgumentException(
+ "UpdateTriggerMode.LostFocus is only supported in Android at this time", nameof(mode)),
+ UpdateTriggerMode.PropertyChanged => CheckControlSource(),
+ _ => this
+ };
}
+ ///
+ /// Define when the binding should be evaluated when the bound target object is a control.
+ ///
+ /// Because Xamarin controls are not DependencyObjects,
+ /// the bound property will not automatically update the binding attached to it.
+ ///
+ /// Instead, use this method to define which of the control's events should be observed.
+ ///
+ ///
+ /// Defines the binding's update mode.
+ ///
+ /// Use to update the binding when the target control's property changes.
+ ///
+ /// The PropertyChanged mode should only be used with the following items:
+ /// - control and its Text property (TextChanged event).
+ /// - control and its Text property (EditingChanged event).
+ /// - control and its On property (ValueChanged event).
+ ///
+ /// The Binding instance.
+ ///
+ /// When this method is called on a OneTime or a OneWay binding.
+ /// This exception can also be thrown when the source object is null or
+ /// has already been garbage collected before this method is called.
+ ///
+ ///
+ /// When is ,
+ /// because it only supported in Android at this time.
+ ///
public Binding ObserveTargetEvent(UpdateTriggerMode mode)
{
- switch (mode)
+ return mode switch
{
- case UpdateTriggerMode.LostFocus:
- throw new ArgumentException(
- "UpdateTriggerMode.LostFocus is only supported in Android at this time",
- nameof(mode));
-
- case UpdateTriggerMode.PropertyChanged:
- return CheckControlTarget();
- }
-
- return this;
+ UpdateTriggerMode.LostFocus => throw new ArgumentException(
+ "UpdateTriggerMode.LostFocus is only supported in Android at this time", nameof(mode)),
+ UpdateTriggerMode.PropertyChanged => CheckControlTarget(),
+ _ => this
+ };
}
+ ///
[SuppressMessage("ReSharper", "RedundantAssignment", Justification = "Reviewed.")]
[SuppressMessage("ReSharper", "EntityNameCapturedOnly.Local", Justification = "Reviewed.")]
protected override Binding CheckControlSource()
{
- switch (PropertySource.Target)
+ switch (_propertySource!.Target)
{
case UITextView textBox:
{
@@ -139,6 +184,7 @@ protected override Binding CheckControlSource()
}
}
+ ///
[SuppressMessage("ReSharper", "RedundantAssignment", Justification = "Reviewed.")]
[SuppressMessage("ReSharper", "EntityNameCapturedOnly.Local", Justification = "Reviewed.")]
protected override Binding CheckControlTarget()
@@ -148,7 +194,7 @@ protected override Binding CheckControlTarget()
return this;
}
- switch (PropertyTarget.Target)
+ switch (_propertyTarget!.Target)
{
case UITextView textBox:
{
diff --git a/Softeq.XToolkit.Bindings.iOS/AppleBindingFactory.cs b/Softeq.XToolkit.Bindings.iOS/AppleBindingFactory.cs
index ad02c3fb5..d0700aeea 100644
--- a/Softeq.XToolkit.Bindings.iOS/AppleBindingFactory.cs
+++ b/Softeq.XToolkit.Bindings.iOS/AppleBindingFactory.cs
@@ -2,14 +2,16 @@
// http://www.softeq.com
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Windows.Input;
using UIKit;
-#nullable disable
-
namespace Softeq.XToolkit.Bindings.iOS
{
+ ///
+ /// The iOS-specific factory to create bindings.
+ ///
public class AppleBindingFactory : BindingFactoryBase
{
///
@@ -17,11 +19,11 @@ public override Binding CreateBinding(
object source,
Expression> sourcePropertyExpression,
bool? resolveTopField,
- object target = null,
- Expression> targetPropertyExpression = null,
+ object? target = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
return new AppleBinding(
source,
@@ -38,11 +40,11 @@ public override Binding CreateBinding(
public override Binding CreateBinding(
object source,
Expression> sourcePropertyExpression,
- object target = null,
- Expression> targetPropertyExpression = null,
+ object? target = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
return new AppleBinding(
source,
@@ -58,11 +60,11 @@ public override Binding CreateBinding(
public override Binding CreateBinding(
object source,
string sourcePropertyName,
- object target = null,
- string targetPropertyName = null,
+ object? target = null,
+ string? targetPropertyName = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
return new AppleBinding(
source,
@@ -74,7 +76,8 @@ public override Binding CreateBinding(
targetNullValue);
}
- public override string GetDefaultEventNameForControl(Type type)
+ ///
+ public override string? GetDefaultEventNameForControl(Type type)
{
if (type == typeof(UIButton) || typeof(UIButton).IsAssignableFrom(type))
{
@@ -94,10 +97,11 @@ public override string GetDefaultEventNameForControl(Type type)
return null;
}
+ ///
public override void HandleCommandCanExecute(
object element,
ICommand command,
- Binding commandParameterBinding)
+ Binding? commandParameterBinding)
{
if (element is UIControl control)
{
@@ -108,7 +112,7 @@ public override void HandleCommandCanExecute(
private static void HandleControlEnabled(
UIControl control,
ICommand command,
- Binding commandParameterBinding)
+ Binding? commandParameterBinding)
{
var commandParameter = commandParameterBinding == null
? default
diff --git a/Softeq.XToolkit.Bindings/Binding.cs b/Softeq.XToolkit.Bindings/Binding.cs
index 24852bb15..db2a146f4 100644
--- a/Softeq.XToolkit.Bindings/Binding.cs
+++ b/Softeq.XToolkit.Bindings/Binding.cs
@@ -21,7 +21,7 @@ public abstract class Binding
protected WeakReference? TopTarget;
///
- /// Occurs when the value of the databound property changes.
+ /// Occurs when the value of the data-bound property changes.
///
public abstract event EventHandler ValueChanged;
@@ -50,20 +50,17 @@ public abstract class Binding
: TopTarget.Target;
///
- /// Instructs the Binding instance to stop listening to value changes and to
- /// remove all listeneners.
+ /// Instructs the Binding instance to stop listening to value changes and to remove all listeners.
///
public abstract void Detach();
///
- /// Forces the Binding's value to be reevaluated. The target value will
- /// be set to the source value.
+ /// Forces the Binding's value to be reevaluated. The target value will be set to the source value.
///
public abstract void ForceUpdateValueFromSourceToTarget();
///
- /// Forces the Binding's value to be reevaluated. The source value will
- /// be set to the target value.
+ /// Forces the Binding's value to be reevaluated. The source value will be set to the target value.
///
public abstract void ForceUpdateValueFromTargetToSource();
}
diff --git a/Softeq.XToolkit.Bindings/BindingExtensions.cs b/Softeq.XToolkit.Bindings/BindingExtensions.cs
index 87f3d7646..1ed231d5d 100644
--- a/Softeq.XToolkit.Bindings/BindingExtensions.cs
+++ b/Softeq.XToolkit.Bindings/BindingExtensions.cs
@@ -2,14 +2,13 @@
// http://www.softeq.com
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using System.Windows.Input;
using Softeq.XToolkit.Common.Commands;
using Softeq.XToolkit.Common.Disposables;
-#nullable disable
-
namespace Softeq.XToolkit.Bindings
{
///
@@ -17,8 +16,19 @@ namespace Softeq.XToolkit.Bindings
///
public static class BindingExtensions
{
- private static IBindingFactory _bindingFactory;
+ private static IBindingFactory? _bindingFactory;
+
+ private static IBindingFactory BindingFactory
+ {
+ get => _bindingFactory
+ ?? throw new InvalidOperationException(
+ $"Please initialize extensions via {nameof(BindingExtensions)}.{nameof(Initialize)} before using.");
+ }
+ ///
+ /// Initializes extensions methods. Should be called at least once before using extension methods.
+ ///
+ /// The factory instance.
public static void Initialize(IBindingFactory bindingFactory)
{
_bindingFactory = bindingFactory;
@@ -42,8 +52,8 @@ public static void Initialize(IBindingFactory bindingFactory)
/// and methods to configure the binding.
/// It is very possible that TSource and TTarget are the same type in which case no conversion occurs.
///
- /// The type of the property that is being databound before conversion.
- /// The type of the property that is being databound after conversion.
+ /// The type of the property that is being data-bound before conversion.
+ /// The type of the property that is being data-bound after conversion.
///
/// The source of the binding. If this object implements
/// and the is OneWay or TwoWay, the target will be notified of changes
@@ -80,16 +90,17 @@ public static void Initialize(IBindingFactory bindingFactory)
///
/// The value used when the source property is null (or equals to default(TSource)).
/// The new Binding instance.
+ /// When class is not initialized.
public static Binding SetBinding(
this object source,
Expression> sourcePropertyExpression,
object target,
- Expression> targetPropertyExpression = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
- return _bindingFactory.CreateBinding(
+ return BindingFactory.CreateBinding(
source,
sourcePropertyExpression,
null,
@@ -137,14 +148,15 @@ public static Binding SetBinding(
/// The value used when the source property is null (or equals to default(TSource)).
/// The type of the bound property.
/// The created binding instance.
+ /// When class is not initialized.
public static Binding SetBinding(
this object source,
Expression> sourcePropertyExpression,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
- return _bindingFactory.CreateBinding(
+ return BindingFactory.CreateBinding(
source,
sourcePropertyExpression,
true,
@@ -165,9 +177,9 @@ public static Binding SetBinding(
/// If the target implements , has observable properties and
/// the is TwoWay, the source will also be notified of changes to the target's properties.
///
- /// The type of the source property that is being databound.
+ /// The type of the source property that is being data-bound.
///
- /// The type of the target property that is being databound. If the source type
+ /// The type of the target property that is being data-bound. If the source type
/// is not the same as the target type, an automatic conversion will be attempted. However only
/// simple types can be converted. For more complex conversions, use the
///
@@ -204,15 +216,16 @@ public static Binding SetBinding(
///
/// The value used when the source property is null (or equals to default(TSource)).
/// The new Binding instance.
+ /// When class is not initialized.
public static Binding SetBinding(
this object source,
Expression> sourcePropertyExpression,
- Expression> targetPropertyExpression = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
- return _bindingFactory.CreateBinding(
+ return BindingFactory.CreateBinding(
source,
sourcePropertyExpression,
null,
@@ -233,9 +246,9 @@ public static Binding SetBinding(
/// raises the PropertyChanged event and the is TwoWay,
/// the source property will also be synchronized with the target property.
///
- /// The type of the source property that is being databound.
+ /// The type of the source property that is being data-bound.
///
- /// The type of the target property that is being databound.
+ /// The type of the target property that is being data-bound.
///
/// If the source type is not the same as the target type, an automatic conversion will be attempted.
/// However only simple types can be converted. For more complex conversions,
@@ -269,16 +282,17 @@ public static Binding SetBinding(
///
/// The value used when the source property is null (or equals to default(TSource)).
/// The new Binding instance.
+ /// When class is not initialized.
public static Binding SetBinding(
this object source,
string sourcePropertyName,
object target,
- string targetPropertyName = null,
+ string? targetPropertyName = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
- return _bindingFactory.CreateBinding(
+ return BindingFactory.CreateBinding(
source,
sourcePropertyName,
target,
@@ -297,9 +311,9 @@ public static Binding SetBinding(
/// If the target implements , has observable properties and
/// the is TwoWay, the source will also be notified of changes to the target's properties.
///
- /// The type of the source property that is being databound.
+ /// The type of the source property that is being data-bound.
///
- /// The type of the target property that is being databound. If the source type is not the same as the target type,
+ /// The type of the target property that is being data-bound. If the source type is not the same as the target type,
/// an automatic conversion will be attempted. However only simple types can be converted.
/// For more complex conversions, use the
/// and methods to define custom converters.
@@ -327,15 +341,16 @@ public static Binding SetBinding(
///
/// The value used when the source property is null (or equals to default(TSource)).
/// The new Binding instance.
+ /// When class is not initialized.
public static Binding SetBinding(
this object source,
string sourcePropertyName,
- string targetPropertyName = null,
+ string? targetPropertyName = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
- return _bindingFactory.CreateBinding(
+ return BindingFactory.CreateBinding(
source,
sourcePropertyName,
null,
@@ -352,6 +367,7 @@ public static Binding SetBinding(
/// The element to which the command is added.
/// The name of the event that will be subscribed to to actuate the command.
/// The command that must be added to the element.
+ /// When class is not initialized.
public static void SetCommand(
this object element,
string eventName,
@@ -360,7 +376,7 @@ public static void SetCommand(
var t = element.GetType();
var e = t.GetEventInfoForControl(eventName);
- var handler = _bindingFactory.GetCommandHandler(e, eventName, t, command);
+ var handler = BindingFactory.GetCommandHandler(e, eventName, t, command);
e.AddEventHandler(element, handler);
@@ -380,6 +396,7 @@ public static void SetCommand(
/// is executed. This is a fixed value. To pass an observable value, use one of the SetCommand
/// overloads that uses a Binding as CommandParameter.
///
+ /// When class is not initialized.
public static void SetCommand(
this object element,
string eventName,
@@ -389,7 +406,7 @@ public static void SetCommand(
var t = element.GetType();
var e = t.GetEventInfoForControl(eventName);
- var handler = _bindingFactory.GetCommandHandler(e, eventName, t, command, commandParameter);
+ var handler = BindingFactory.GetCommandHandler(e, eventName, t, command, commandParameter);
e.AddEventHandler(element, handler);
@@ -410,18 +427,19 @@ public static void SetCommand(
/// Depending on the , the CommandParameter will be observed and changes
/// will be passed to the command, for example to update the CanExecute.
///
+ /// When class is not initialized.
public static void SetCommand(
this object element,
string eventName,
ICommand command,
- Binding commandParameterBinding)
+ Binding? commandParameterBinding)
{
var t = element.GetType();
var e = t.GetEventInfoForControl(eventName);
- var castedBinding = (Binding) commandParameterBinding;
+ var castedBinding = commandParameterBinding as Binding;
- var handler = _bindingFactory.GetCommandHandler(e, eventName, t, command, castedBinding);
+ var handler = BindingFactory.GetCommandHandler(e, eventName, t, command, castedBinding);
e.AddEventHandler(element, handler);
@@ -441,6 +459,7 @@ public static void SetCommand(
/// The element to which the command is added.
/// The name of the event that will be subscribed to to actuate the command.
/// The command that must be added to the element.
+ /// When class is not initialized.
public static void SetCommand(
this object element,
string eventName,
@@ -449,7 +468,7 @@ public static void SetCommand(
var t = element.GetType();
var e = t.GetEventInfoForControl(eventName);
- var handler = _bindingFactory.GetCommandHandler(e, eventName, t, command);
+ var handler = BindingFactory.GetCommandHandler(e, eventName, t, command);
e.AddEventHandler(element, handler);
@@ -470,6 +489,7 @@ public static void SetCommand(
/// is executed. This is a fixed value. To pass an observable value, use one of the SetCommand
/// overloads that uses a Binding as CommandParameter.
///
+ /// When class is not initialized.
public static void SetCommand(
this object element,
string eventName,
@@ -479,7 +499,7 @@ public static void SetCommand(
var t = element.GetType();
var e = t.GetEventInfoForControl(eventName);
- var handler = _bindingFactory.GetCommandHandler(e, eventName, t, command, commandParameter);
+ var handler = BindingFactory.GetCommandHandler(e, eventName, t, command, commandParameter);
e.AddEventHandler(element, handler);
@@ -500,18 +520,19 @@ public static void SetCommand(
/// that will passed to the . Depending on the Binding, the CommandParameter will be observed
/// and changes will be passed to the command, for example to update the CanExecute.
///
+ /// When class is not initialized.
public static void SetCommand(
this object element,
string eventName,
ICommand command,
- Binding commandParameterBinding)
+ Binding? commandParameterBinding)
{
var t = element.GetType();
var e = t.GetEventInfoForControl(eventName);
- var castedBinding = (Binding) commandParameterBinding;
+ var castedBinding = commandParameterBinding as Binding;
- var handler = _bindingFactory.GetCommandHandler(e, eventName, t, command, castedBinding);
+ var handler = BindingFactory.GetCommandHandler(e, eventName, t, command, castedBinding);
e.AddEventHandler(element, handler);
@@ -531,6 +552,7 @@ public static void SetCommand(
/// The name of the event that will be subscribed to to actuate the command.
/// The command that must be added to the element.
/// instance for manual unset/unsubscribe of command.
+ /// When class is not initialized.
public static IDisposable SetCommandWithDisposing(
this object element,
string eventName,
@@ -539,7 +561,7 @@ public static IDisposable SetCommandWithDisposing(
var t = element.GetType();
var e = t.GetEventInfoForControl(eventName);
- var handler = _bindingFactory.GetCommandHandler(e, eventName, t, command);
+ var handler = BindingFactory.GetCommandHandler(e, eventName, t, command);
e.AddEventHandler(element, handler);
@@ -615,11 +637,11 @@ public static void SetCommand(
SetCommand(element, eventName, new RelayCommand(action));
}
- internal static EventInfo GetEventInfoForControl(this Type type, string eventName)
+ internal static EventInfo GetEventInfoForControl(this Type type, string? eventName)
{
if (string.IsNullOrEmpty(eventName))
{
- eventName = _bindingFactory.GetDefaultEventNameForControl(type);
+ eventName = BindingFactory.GetDefaultEventNameForControl(type);
}
if (string.IsNullOrEmpty(eventName))
@@ -647,9 +669,9 @@ private static void HandleCommandCanExecute(
private static void HandleCommandCanExecute(
object element,
ICommand command,
- Binding commandParameterBinding = null)
+ Binding? commandParameterBinding = null)
{
- _bindingFactory.HandleCommandCanExecute(element, command, commandParameterBinding);
+ BindingFactory.HandleCommandCanExecute(element, command, commandParameterBinding);
}
}
}
diff --git a/Softeq.XToolkit.Bindings/BindingFactoryBase.cs b/Softeq.XToolkit.Bindings/BindingFactoryBase.cs
index 1b66ce368..d686f50eb 100644
--- a/Softeq.XToolkit.Bindings/BindingFactoryBase.cs
+++ b/Softeq.XToolkit.Bindings/BindingFactoryBase.cs
@@ -2,12 +2,16 @@
// http://www.softeq.com
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using System.Windows.Input;
namespace Softeq.XToolkit.Bindings
{
+ ///
+ /// The base factory to create bindings.
+ ///
public abstract class BindingFactoryBase : IBindingFactory
{
///
@@ -18,8 +22,8 @@ public abstract Binding CreateBinding(
object? target = null,
Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default);
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!);
///
public abstract Binding CreateBinding(
@@ -28,8 +32,8 @@ public abstract Binding CreateBinding(
object? target = null,
Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default);
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!);
///
public abstract Binding CreateBinding(
@@ -38,17 +42,17 @@ public abstract Binding CreateBinding(
object? target = null,
string? targetPropertyName = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default);
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!);
///
- public abstract string GetDefaultEventNameForControl(Type type);
+ public abstract string? GetDefaultEventNameForControl(Type type);
///
public abstract void HandleCommandCanExecute(
object element,
ICommand command,
- Binding commandParameterBinding);
+ Binding? commandParameterBinding);
///
public virtual Delegate GetCommandHandler(
@@ -74,11 +78,11 @@ public virtual Delegate GetCommandHandler(
string eventName,
Type elementType,
ICommand command,
- Binding castedBinding)
+ Binding? castedBinding)
{
EventHandler handler = (_, __) =>
{
- object param = (castedBinding == null ? default : castedBinding.Value)!;
+ var param = castedBinding == null ? default : castedBinding.Value;
if (command.CanExecute(param))
{
@@ -129,11 +133,11 @@ public virtual Delegate GetCommandHandler(
string eventName,
Type elementType,
ICommand command,
- Binding castedBinding)
+ Binding? castedBinding)
{
EventHandler handler = (_, __) =>
{
- object param = (castedBinding == null ? default : castedBinding.Value)!;
+ var param = castedBinding == null ? default : castedBinding.Value;
if (command.CanExecute(param))
{
diff --git a/Softeq.XToolkit.Bindings/BindingGeneric.cs b/Softeq.XToolkit.Bindings/BindingGeneric.cs
index 043bd5d8c..a4ee7f206 100644
--- a/Softeq.XToolkit.Bindings/BindingGeneric.cs
+++ b/Softeq.XToolkit.Bindings/BindingGeneric.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@@ -11,51 +12,75 @@
using Softeq.XToolkit.Common.Converters;
using Softeq.XToolkit.Common.Weak;
-#nullable disable
-
-#pragma warning disable SA1649, SA1201, SA1204
-
namespace Softeq.XToolkit.Bindings
{
///
/// Creates a binding between two properties.
///
- /// If the source implements , the source property raises the PropertyChanged event and
- /// the is OneWay or TwoWay, the target property will be synchronized with the source property.
+ /// If the source implements ,
+ /// the source property raises the PropertyChanged event and the is OneWay or TwoWay,
+ /// the target property will be synchronized with the source property.
///
- /// If the target implements , the target property raises the PropertyChanged event
- /// and the is TwoWay, the source property will also be synchronized with the target property.
+ /// If the target implements ,
+ /// the target property raises the PropertyChanged event and the is TwoWay,
+ /// the source property will also be synchronized with the target property.
///
- /// The type of the source property that is being databound.
+ /// The type of the source property that is being data-bound.
///
- /// The type of the target property that is being databound. If the source type
+ /// The type of the target property that is being data-bound. If the source type
/// is not the same as the target type, an automatic conversion will be attempted. However only
/// simple types can be converted. For more complex conversions, use the
/// and methods to define custom converters.
///
+#pragma warning disable SA1649
public abstract class Binding : Binding
+#pragma warning restore SA1649
{
- private readonly SimpleConverter _converter = new SimpleConverter();
+ private readonly WeakConverter _converter = new WeakConverter();
private readonly List _listeners = new List();
- private readonly Expression> _sourcePropertyExpression;
- private readonly Func _sourcePropertyFunc;
- private readonly string _sourcePropertyName;
- private readonly Expression> _targetPropertyExpression;
- private readonly string _targetPropertyName;
+
+ private readonly string? _sourcePropertyName;
+ private readonly string? _targetPropertyName;
+
+ private readonly Expression>? _sourcePropertyExpression;
+ private readonly Expression>? _targetPropertyExpression;
+
+ private readonly Func? _sourcePropertyFunc;
+
+ ///
+ /// The source property handlers.
+ ///
public readonly Dictionary SourceHandlers = new Dictionary();
+
+ ///
+ /// The target property handlers.
+ ///
public readonly Dictionary TargetHandlers = new Dictionary();
+
private bool _isFallbackValueActive;
- private WeakAction _onSourceUpdate;
- private WeakAction _onSourceUpdateWithParameter;
private bool _resolveTopField;
+
private bool _settingSourceToTarget;
private bool _settingTargetToSource;
- private PropertyInfo _sourceProperty;
- private WeakFunc _sourceUpdateFunctionWithParameter;
- private PropertyInfo _targetProperty;
- private IConverter _valueConverter;
- protected WeakReference PropertySource;
- protected WeakReference PropertyTarget;
+
+ private PropertyInfo? _sourceProperty;
+ private PropertyInfo? _targetProperty;
+
+ private WeakAction? _onSourceUpdate;
+ private WeakAction? _onSourceUpdateWithParameter;
+ private WeakFunc? _onSourceUpdateFunctionWithParameter;
+
+ private IConverter? _valueConverter;
+
+ ///
+ /// A weak reference to the source property.
+ ///
+ protected WeakReference? _propertySource;
+
+ ///
+ /// A weak reference to the target property.
+ ///
+ protected WeakReference? _propertyTarget;
///
/// Initializes a new instance of the class for
@@ -73,47 +98,46 @@ public abstract class Binding : Binding
/// The name of the target property for the binding.
///
/// The mode of the binding. OneTime means that the target property will be set once (when the binding is
- /// created) but that subsequent changes will be ignored. OneWay means that the target property will be set, and
- /// if the PropertyChanged event is raised by the source, the target property will be updated. TwoWay means that the
- /// source
- /// property will also be updated if the target raises the PropertyChanged event. Default means OneWay if only the
- /// source
- /// implements INPC, and TwoWay if both the source and the target implement INPC.
+ /// created) but that subsequent changes will be ignored. OneWay means that the target property will be set,
+ /// and if the PropertyChanged event is raised by the source, the target property will be updated.
+ /// TwoWay means that the source property will also be updated if the target raises the PropertyChanged event.
+ /// Default means OneWay if only the source implements INPC,
+ /// and TwoWay if both the source and the target implement INPC.
///
///
- /// Tthe value to use when the binding is unable to return a value. This can happen if one of the
+ /// The value to use when the binding is unable to return a value. This can happen if one of the
/// items on the Path (except the source property itself) is null, or if the Converter throws an exception.
///
///
- /// The value to use when the binding is unable to return a value. This can happen if one of the
+ /// The target value to use when the binding is unable to return a value. This can happen if one of the
/// items on the Path (except the source property itself) is null, or if the Converter throws an exception.
///
protected Binding(
object source,
string sourcePropertyName,
- object target = null,
- string targetPropertyName = null,
+ object? target = null,
+ string? targetPropertyName = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
Mode = mode;
FallbackValue = fallbackValue;
TargetNullValue = targetNullValue;
TopSource = new WeakReference(source);
- PropertySource = new WeakReference(source);
+ _propertySource = new WeakReference(source);
_sourcePropertyName = sourcePropertyName;
if (target == null)
{
TopTarget = TopSource;
- PropertyTarget = PropertySource;
+ _propertyTarget = _propertySource;
}
else
{
TopTarget = new WeakReference(target);
- PropertyTarget = new WeakReference(target);
+ _propertyTarget = new WeakReference(target);
}
_targetPropertyName = targetPropertyName;
@@ -121,7 +145,7 @@ protected Binding(
}
///
- /// Initializes a new instance of the class for
+ /// Initializes a new instance of the class for
/// which the source and target properties are located in different objects.
///
///
@@ -157,17 +181,15 @@ protected Binding(
/// The value to use when the binding is unable to return a value. This can happen if one of the
/// items on the Path (except the source property itself) is null, or if the Converter throws an exception.
///
- ///
- /// The value to use when the binding is unable to return a value.
- ///
+ /// The value to use when the binding is unable to return a value.
protected Binding(
object source,
Expression> sourcePropertyExpression,
- object target = null,
- Expression> targetPropertyExpression = null,
+ object? target = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
: this(
source,
sourcePropertyExpression,
@@ -180,15 +202,16 @@ protected Binding(
{
}
+ ///
protected Binding(
object source,
Expression> sourcePropertyExpression,
bool? resolveTopField,
- object target = null,
- Expression> targetPropertyExpression = null,
+ object? target = null,
+ Expression>? targetPropertyExpression = null,
BindingMode mode = BindingMode.Default,
- TSource fallbackValue = default,
- TSource targetNullValue = default)
+ [MaybeNull] TSource fallbackValue = default!,
+ [MaybeNull] TSource targetNullValue = default!)
{
Mode = mode;
FallbackValue = fallbackValue;
@@ -210,6 +233,11 @@ protected Binding(
resolveTopField ?? target == null && targetPropertyExpression != null);
}
+ ///
+ /// Occurs when the value of the data-bound property changes.
+ ///
+ public override event EventHandler? ValueChanged;
+
///
/// Gets the value to use when the binding is unable to return a value. This can happen if one of the
/// items on the Path (except the source property itself) is null, or if the Converter throws an exception.
@@ -224,31 +252,30 @@ protected Binding(
///
/// Gets the current value of the binding.
///
+ [MaybeNull]
public TTarget Value
{
get
{
- if (PropertySource == null
- || !PropertySource.IsAlive)
+ if (!(_propertySource is { IsAlive: true }))
{
return default;
}
- var type = PropertySource.Target.GetType();
+ var type = _propertySource.Target.GetType();
var property = type.GetRuntimeProperty(_sourcePropertyName);
- return (TTarget) property.GetValue(PropertySource.Target, null);
+ return (TTarget) property.GetValue(_propertySource.Target, null);
}
}
///
- /// Defines a custom conversion method for a binding. To be used when the
- /// binding's source property is of a different type than the binding's
- /// target property, and the conversion cannot be done automatically (simple
- /// values).
+ /// Defines a custom conversion method for a binding.
+ ///
+ /// To be used when the binding's source property is of a different type than the binding's
+ /// target property, and the conversion cannot be done automatically (simple values).
///
///
- /// A func that will be called with the source
- /// property's value, and will return the target property's value.
+ /// A func that will be called with the source property's value, and will return the target property's value.
/// IMPORTANT: Note that closures are not supported at the moment
/// due to the use of WeakActions (see http://stackoverflow.com/questions/25730530/).
///
@@ -261,14 +288,13 @@ public Binding ConvertSourceToTarget(Func co
}
///
- /// Defines a custom conversion method for a two-way binding. To be used when the
- /// binding's target property is of a different type than the binding's
- /// source property, and the conversion cannot be done automatically (simple
- /// values).
+ /// Defines a custom conversion method for a two-way binding.
+ ///
+ /// To be used when the binding's target property is of a different type than the binding's
+ /// source property, and the conversion cannot be done automatically (simple values).
///
///
- /// A func that will be called with the source
- /// property's value, and will return the target property's value.
+ /// A func that will be called with the source property's value, and will return the target property's value.
/// IMPORTANT: Note that closures are not supported at the moment
/// due to the use of WeakActions (see http://stackoverflow.com/questions/25730530/).
///
@@ -280,7 +306,23 @@ public Binding ConvertTargetToSource(Func co
return this;
}
- public Binding SetConverter(IConverter converter)
+ ///
+ /// Defines a custom conversion methods for a binding.
+ ///
+ /// To be used when the binding's source/target property is of a different type than the binding's
+ /// target/source property, and the conversion cannot be done automatically (simple values).
+ ///
+ ///
+ /// The instance of the converter, where:
+ ///
+ /// - will have behavior like method.
+ ///
+ ///
+ /// - will have behavior like method.
+ ///
+ ///
+ /// The Binding instance.
+ public Binding SetConverter(IConverter? converter)
{
if (converter == null)
{
@@ -307,8 +349,8 @@ public override void Detach()
_listeners.Clear();
- DetachSourceHandlers();
- DetachTargetHandlers();
+ DetachAllSourceHandlers();
+ DetachAllTargetHandlers();
}
///
@@ -318,9 +360,9 @@ public override void ForceUpdateValueFromSourceToTarget()
{
if (_onSourceUpdate == null
&& _onSourceUpdateWithParameter == null
- && (PropertySource == null
- || !PropertySource.IsAlive
- || PropertySource.Target == null))
+ && (_propertySource == null
+ || !_propertySource.IsAlive
+ || _propertySource.Target == null))
{
return;
}
@@ -330,12 +372,12 @@ public override void ForceUpdateValueFromSourceToTarget()
try
{
var value = GetSourceValue();
- var targetValue = _targetProperty.GetValue(PropertyTarget.Target);
+ var targetValue = _targetProperty.GetValue(_propertyTarget!.Target);
if (!Equals(value, targetValue))
{
_settingSourceToTarget = true;
- SetTargetValue(value);
+ SetTargetValue(value!);
_settingSourceToTarget = false;
}
}
@@ -344,22 +386,20 @@ public override void ForceUpdateValueFromSourceToTarget()
if (!Equals(FallbackValue, default(TSource)))
{
_settingSourceToTarget = true;
- _targetProperty.SetValue(PropertyTarget.Target, FallbackValue, null);
+ _targetProperty.SetValue(_propertyTarget!.Target, FallbackValue, null);
_settingSourceToTarget = false;
}
}
}
- if (_onSourceUpdate != null
- && _onSourceUpdate.IsAlive)
+ if (_onSourceUpdate is { IsAlive: true })
{
_onSourceUpdate.Execute();
}
- if (_onSourceUpdateWithParameter != null
- && _onSourceUpdateWithParameter.IsAlive)
+ if (_onSourceUpdateWithParameter is { IsAlive: true })
{
- _onSourceUpdateWithParameter.Execute(_sourcePropertyFunc.Invoke());
+ _onSourceUpdateWithParameter.Execute(_sourcePropertyFunc!.Invoke());
}
RaiseValueChanged();
@@ -370,12 +410,12 @@ public override void ForceUpdateValueFromSourceToTarget()
///
public override void ForceUpdateValueFromTargetToSource()
{
- if (PropertyTarget == null
- || !PropertyTarget.IsAlive
- || PropertyTarget.Target == null
- || PropertySource == null
- || !PropertySource.IsAlive
- || PropertySource.Target == null)
+ if (_propertyTarget == null
+ || !_propertyTarget.IsAlive
+ || _propertyTarget.Target == null
+ || _propertySource == null
+ || !_propertySource.IsAlive
+ || _propertySource.Target == null)
{
return;
}
@@ -383,12 +423,12 @@ public override void ForceUpdateValueFromTargetToSource()
if (_targetProperty != null)
{
var value = GetTargetValue();
- var sourceValue = _sourceProperty.GetValue(PropertySource.Target);
+ var sourceValue = _sourceProperty!.GetValue(_propertySource.Target);
if (!Equals(value, sourceValue))
{
_settingTargetToSource = true;
- SetSourceValue(value);
+ SetSourceValue(value!);
_settingTargetToSource = false;
}
}
@@ -397,10 +437,12 @@ public override void ForceUpdateValueFromTargetToSource()
}
///
- /// Define when the binding should be evaluated when the bound source object
- /// is a control. Because Xamarin controls are not DependencyObjects, the
- /// bound property will not automatically update the binding attached to it. Instead,
- /// use this method to define which of the control's events should be observed.
+ /// Define when the binding should be evaluated when the bound source object is a control.
+ ///
+ /// Because Xamarin controls are not DependencyObjects,
+ /// the bound property will not automatically update the binding attached to it.
+ ///
+ /// Instead, use this method to define which of the control's events should be observed.
///
///
/// The name of the event that should be observed to update the binding's value.
@@ -431,9 +473,9 @@ public Binding ObserveSourceEvent(string eventName)
if (_onSourceUpdate == null
&& _onSourceUpdateWithParameter == null
- && (PropertySource == null
- || !PropertySource.IsAlive
- || PropertySource.Target == null))
+ && (_propertySource == null
+ || !_propertySource.IsAlive
+ || _propertySource.Target == null))
{
throw new InvalidOperationException("Source is not ready");
}
@@ -443,20 +485,19 @@ public Binding ObserveSourceEvent(string eventName)
throw new ArgumentNullException(nameof(eventName));
}
- var type = PropertySource.Target.GetType();
- var ev = type.GetRuntimeEvent(eventName);
- if (ev == null)
+ var type = _propertySource!.Target.GetType();
+ var @event = type.GetRuntimeEvent(eventName);
+ if (@event == null)
{
- throw new ArgumentException("Event not found: " + eventName, nameof(eventName));
+ throw new ArgumentException($"Event not found: {eventName}", nameof(eventName));
}
EventHandler handler = HandleSourceEvent;
- var defaultHandlerInfo = SourceHandlers.Values.FirstOrDefault(i => i.IsDefault);
-
+ var defaultHandlerInfo = SourceHandlers.Values.FirstOrDefault(x => x.IsDefault);
if (defaultHandlerInfo != null)
{
- DetachSourceHandlers();
+ DetachAllSourceHandlers();
}
var info = new DelegateInfo
@@ -473,34 +514,28 @@ public Binding ObserveSourceEvent(string eventName)
SourceHandlers.Add(eventName, info);
}
- ev.AddEventHandler(
- PropertySource.Target,
- handler);
+ @event.AddEventHandler(_propertySource.Target, handler);
return this;
}
///
- /// Define when the binding should be evaluated when the bound source object
- /// is a control. Because Xamarin controls are not DependencyObjects, the
- /// bound property will not automatically update the binding attached to it. Instead,
- /// use this method to define which of the control's events should be observed.
+ /// Define when the binding should be evaluated when the bound source object is a control.
+ ///
+ /// Because Xamarin controls are not DependencyObjects,
+ /// the bound property will not automatically update the binding attached to it.
+ ///
+ /// Instead, use this method to define which of the control's events should be observed.
///
///
- /// Use this method when the event requires a specific EventArgs type
- /// instead of the standard EventHandler.
+ /// Use this method when the event requires a specific EventArgs type instead of the standard EventHandler.
///
/// The type of the EventArgs used by this control's event.
- ///
- /// The name of the event that should be observed
- /// to update the binding's value.
- ///
+ /// The name of the event that should be observed to update the binding's value.
/// The Binding instance.
///
- /// When this method is called
- /// on a OneTime binding. Such bindings cannot be updated. This exception can
- /// also be thrown when the source object is null or has already been
- /// garbage collected before this method is called.
+ /// When this method is called on a OneTime binding. Such bindings cannot be updated. This exception can
+ /// also be thrown when the source object is null or has already been garbage collected before this method is called.
///
///
/// When the eventName parameter is null or is an empty string.
@@ -524,9 +559,9 @@ public Binding ObserveSourceEvent(string eventName
if (_onSourceUpdate == null
&& _onSourceUpdateWithParameter == null
- && (PropertySource == null
- || !PropertySource.IsAlive
- || PropertySource.Target == null))
+ && (_propertySource == null
+ || !_propertySource.IsAlive
+ || _propertySource.Target == null))
{
throw new InvalidOperationException("Source is not ready");
}
@@ -536,20 +571,19 @@ public Binding ObserveSourceEvent(string eventName
throw new ArgumentNullException(nameof(eventName));
}
- var type = PropertySource.Target.GetType();
- var ev = type.GetRuntimeEvent(eventName);
- if (ev == null)
+ var type = _propertySource!.Target.GetType();
+ var @event = type.GetRuntimeEvent(eventName);
+ if (@event == null)
{
- throw new ArgumentException("Event not found: " + eventName, nameof(eventName));
+ throw new ArgumentException($"Event not found: {eventName}", nameof(eventName));
}
EventHandler handler = HandleSourceEvent;
- var defaultHandlerInfo = SourceHandlers.Values.FirstOrDefault(i => i.IsDefault);
-
+ var defaultHandlerInfo = SourceHandlers.Values.FirstOrDefault(x => x.IsDefault);
if (defaultHandlerInfo != null)
{
- DetachSourceHandlers();
+ DetachAllSourceHandlers();
}
var info = new DelegateInfo
@@ -566,29 +600,24 @@ public Binding ObserveSourceEvent(string eventName
SourceHandlers.Add(eventName, info);
}
- ev.AddEventHandler(
- PropertySource.Target,
- handler);
+ @event.AddEventHandler(_propertySource.Target, handler);
return this;
}
///
- /// Define when the binding should be evaluated when the bound target object
- /// is a control. Because Xamarin controls are not DependencyObjects, the
- /// bound property will not automatically update the binding attached to it. Instead,
- /// use this method to define which of the control's events should be observed.
+ /// Define when the binding should be evaluated when the bound target object is a control.
+ ///
+ /// Because Xamarin controls are not DependencyObjects,
+ /// the bound property will not automatically update the binding attached to it.
+ ///
+ /// Instead, use this method to define which of the control's events should be observed.
///
- ///
- /// The name of the event that should be observed
- /// to update the binding's value.
- ///
+ /// The name of the event that should be observed to update the binding's value.
/// The Binding instance.
///
- /// When this method is called
- /// on a OneTime or a OneWay binding. This exception can
- /// also be thrown when the source object is null or has already been
- /// garbage collected before this method is called.
+ /// When this method is called on a OneTime or a OneWay binding. This exception can
+ /// also be thrown when the source object is null or has already been garbage collected before this method is called.
///
///
/// When the eventName parameter is null or is an empty string.
@@ -619,9 +648,9 @@ public Binding ObserveTargetEvent(string eventName)
throw new InvalidOperationException("Cannot use SetTargetEvent with onSourceUpdate");
}
- if (PropertyTarget == null
- || !PropertyTarget.IsAlive
- || PropertyTarget.Target == null)
+ if (_propertyTarget == null
+ || !_propertyTarget.IsAlive
+ || _propertyTarget.Target == null)
{
throw new InvalidOperationException("Target is not ready");
}
@@ -631,21 +660,20 @@ public Binding ObserveTargetEvent(string eventName)
throw new ArgumentNullException(nameof(eventName));
}
- var type = PropertyTarget.Target.GetType();
+ var type = _propertyTarget.Target.GetType();
- var ev = type.GetRuntimeEvent(eventName);
- if (ev == null)
+ var @event = type.GetRuntimeEvent(eventName);
+ if (@event == null)
{
- throw new ArgumentException("Event not found: " + eventName, nameof(eventName));
+ throw new ArgumentException($"Event not found: {eventName}", nameof(eventName));
}
EventHandler handler = HandleTargetEvent;
- var defaultHandlerInfo = TargetHandlers.Values.FirstOrDefault(i => i.IsDefault);
-
+ var defaultHandlerInfo = TargetHandlers.Values.FirstOrDefault(x => x.IsDefault);
if (defaultHandlerInfo != null)
{
- DetachTargetHandlers();
+ DetachAllTargetHandlers();
}
var info = new DelegateInfo
@@ -662,34 +690,28 @@ public Binding ObserveTargetEvent(string eventName)
TargetHandlers.Add(eventName, info);
}
- ev.AddEventHandler(
- PropertyTarget.Target,
- handler);
+ @event.AddEventHandler(_propertyTarget.Target, handler);
return this;
}
///
- /// Define when the binding should be evaluated when the bound target object
- /// is a control. Because Xamarin controls are not DependencyObjects, the
- /// bound property will not automatically update the binding attached to it. Instead,
- /// use this method to define which of the control's events should be observed.
+ /// Define when the binding should be evaluated when the bound target object is a control.
+ ///
+ /// Because Xamarin controls are not DependencyObjects,
+ /// the bound property will not automatically update the binding attached to it.
+ ///
+ /// Instead, use this method to define which of the control's events should be observed.
///
///
- /// Use this method when the event requires a specific EventArgs type
- /// instead of the standard EventHandler.
+ /// Use this method when the event requires a specific EventArgs type instead of the standard EventHandler.
///
/// The type of the EventArgs used by this control's event.
- ///
- /// The name of the event that should be observed
- /// to update the binding's value.
- ///
+ /// The name of the event that should be observed to update the binding's value.
/// The Binding instance.
///
- /// When this method is called
- /// on a OneTime or OneWay binding. This exception can
- /// also be thrown when the target object is null or has already been
- /// garbage collected before this method is called.
+ /// When this method is called on a OneTime or OneWay binding. This exception can
+ /// also be thrown when the target object is null or has already been garbage collected before this method is called.
///
///
/// When the eventName parameter is null or is an empty string.
@@ -721,9 +743,9 @@ public Binding ObserveTargetEvent(string eventName
throw new InvalidOperationException("Cannot use SetTargetEvent with onSourceUpdate");
}
- if (PropertyTarget == null
- || !PropertyTarget.IsAlive
- || PropertyTarget.Target == null)
+ if (_propertyTarget == null
+ || !_propertyTarget.IsAlive
+ || _propertyTarget.Target == null)
{
throw new InvalidOperationException("Target is not ready");
}
@@ -733,21 +755,20 @@ public Binding ObserveTargetEvent(string eventName
throw new ArgumentNullException(nameof(eventName));
}
- var type = PropertyTarget.Target.GetType();
+ var type = _propertyTarget.Target.GetType();
- var ev = type.GetRuntimeEvent(eventName);
- if (ev == null)
+ var @event = type.GetRuntimeEvent(eventName);
+ if (@event == null)
{
- throw new ArgumentException("Event not found: " + eventName, nameof(eventName));
+ throw new ArgumentException($"Event not found: {eventName}", nameof(eventName));
}
EventHandler handler = HandleTargetEvent;
- var defaultHandlerInfo = TargetHandlers.Values.FirstOrDefault(i => i.IsDefault);
-
+ var defaultHandlerInfo = TargetHandlers.Values.FirstOrDefault(x => x.IsDefault);
if (defaultHandlerInfo != null)
{
- DetachTargetHandlers();
+ DetachAllTargetHandlers();
}
var info = new DelegateInfo
@@ -764,9 +785,7 @@ public Binding ObserveTargetEvent(string eventName
TargetHandlers.Add(eventName, info);
}
- ev.AddEventHandler(
- PropertyTarget.Target,
- handler);
+ @event.AddEventHandler(_propertyTarget.Target, handler);
return this;
}
@@ -801,6 +820,18 @@ public Binding WhenSourceChanges(Action callback)
return this;
}
+ ///
+ /// Defines an generic action that will be executed every time that the binding value changes.
+ ///
+ ///
+ /// The generic action that will be executed when the binding changes.
+ /// IMPORTANT: Note that closures are not supported at the moment
+ /// due to the use of WeakActions (see http://stackoverflow.com/questions/25730530/).
+ ///
+ /// The Binding instance.
+ ///
+ /// When the method is called on a binding that already has a target property set.
+ ///
public Binding WhenSourceChanges(Action callback)
{
if (_targetPropertyExpression != null)
@@ -813,53 +844,66 @@ public Binding WhenSourceChanges(Action callback)
if (_onSourceUpdateWithParameter.IsAlive)
{
- _onSourceUpdateWithParameter.Execute(_sourcePropertyFunc.Invoke());
+ _onSourceUpdateWithParameter.Execute(_sourcePropertyFunc!.Invoke());
}
return this;
}
- public Binding WhenSourceChanges(Func func)
+ ///
+ /// Defines an generic function that will be executed every time that the binding value changes.
+ ///
+ ///
+ /// The function that will be executed when the binding changes.
+ /// IMPORTANT: Note that closures are not supported at the moment
+ /// due to the use of WeakActions (see http://stackoverflow.com/questions/25730530/).
+ ///
+ /// The Binding instance.
+ ///
+ /// When the method is called on a binding that already has a target property set.
+ ///
+ public Binding WhenSourceChanges(Func callback)
{
- _sourceUpdateFunctionWithParameter = new WeakFunc(func);
+ _onSourceUpdateFunctionWithParameter = new WeakFunc(callback);
return WhenSourceChanges(source =>
{
- _sourceUpdateFunctionWithParameter.Execute(_sourcePropertyFunc.Invoke());
+ _onSourceUpdateFunctionWithParameter.Execute(_sourcePropertyFunc!.Invoke());
});
}
+ ///
+ /// Defines the behavior to update the binding when the source control's property changes.
+ ///
+ /// The Binding instance.
protected abstract Binding CheckControlSource();
+ ///
+ /// Defines the behavior to update the binding when the source control's property changes.
+ ///
+ /// The Binding instance.
protected abstract Binding CheckControlTarget();
- private void Attach(
- object source,
- object target,
- BindingMode mode)
+ private void Attach(object? source, object? target, BindingMode mode)
{
Attach(source, target, mode, _resolveTopField);
}
- private void Attach(
- object source,
- object target,
- BindingMode mode,
- bool resolveTopField)
+ private void Attach(object? source, object? target, BindingMode mode, bool resolveTopField)
{
_resolveTopField = resolveTopField;
var sourceChain = GetPropertyChain(
source,
null,
- _sourcePropertyExpression.Body as MemberExpression,
- _sourcePropertyName,
+ _sourcePropertyExpression!.Body as MemberExpression,
+ _sourcePropertyName!,
resolveTopField);
var lastSourceInChain = sourceChain.Last();
sourceChain.Remove(lastSourceInChain);
- PropertySource = new WeakReference(lastSourceInChain.Instance);
+ _propertySource = new WeakReference(lastSourceInChain.Instance);
if (mode != BindingMode.OneTime)
{
@@ -888,7 +932,7 @@ private void Attach(
var lastTargetInChain = targetChain.Last();
targetChain.Remove(lastTargetInChain);
- PropertyTarget = new WeakReference(lastTargetInChain.Instance);
+ _propertyTarget = new WeakReference(lastTargetInChain.Instance);
if (mode != BindingMode.OneTime)
{
@@ -923,34 +967,35 @@ private void Attach(
private void Attach()
{
- if (PropertyTarget != null
- && PropertyTarget.IsAlive
- && PropertyTarget.Target != null
+ if (_propertyTarget != null
+ && _propertyTarget.IsAlive
+ && _propertyTarget.Target != null
&& !string.IsNullOrEmpty(_targetPropertyName))
{
- var targetType = PropertyTarget.Target.GetType();
- _targetProperty = targetType.GetRuntimeProperty(_targetPropertyName);
+ var targetType = _propertyTarget.Target.GetType();
+ _targetProperty = targetType.GetRuntimeProperty(_targetPropertyName);
if (_targetProperty == null)
{
- throw new InvalidOperationException("Property not found: " + _targetPropertyName);
+ throw new InvalidOperationException($"Property not found: {_targetPropertyName}");
}
}
- if (PropertySource == null
- || !PropertySource.IsAlive
- || PropertySource.Target == null)
+ if (_propertySource == null
+ || !_propertySource.IsAlive
+ || _propertySource.Target == null)
{
SetSpecialValues();
return;
}
- var sourceType = PropertySource.Target.GetType();
+ var sourceType = _propertySource.Target.GetType();
+
_sourceProperty = sourceType.GetRuntimeProperty(_sourcePropertyName);
if (_sourceProperty == null)
{
- throw new InvalidOperationException("Property not found: " + _sourcePropertyName);
+ throw new InvalidOperationException($"Property not found: {_sourcePropertyName}");
}
// OneTime binding
@@ -959,12 +1004,12 @@ private void Attach()
var value = GetSourceValue();
if (_targetProperty != null
- && PropertyTarget != null
- && PropertyTarget.IsAlive
- && PropertyTarget.Target != null)
+ && _propertyTarget != null
+ && _propertyTarget.IsAlive
+ && _propertyTarget.Target != null)
{
_settingSourceToTarget = true;
- SetTargetValue(value);
+ SetTargetValue(value!);
_settingSourceToTarget = false;
}
@@ -977,7 +1022,7 @@ private void Attach()
if (_onSourceUpdateWithParameter != null
&& _onSourceUpdateWithParameter.IsAlive)
{
- _onSourceUpdateWithParameter.Execute(_sourcePropertyFunc.Invoke());
+ _onSourceUpdateWithParameter.Execute(_sourcePropertyFunc!.Invoke());
}
}
@@ -987,7 +1032,7 @@ private void Attach()
}
// Check OneWay binding
- if (PropertySource.Target is INotifyPropertyChanged inpc)
+ if (_propertySource.Target is INotifyPropertyChanged inpc)
{
var listener = new PropertyChangedEventListener(this, inpc, true);
_listeners.Add(listener);
@@ -1007,11 +1052,11 @@ private void Attach()
// Check TwoWay binding
if (_onSourceUpdate == null
&& _onSourceUpdateWithParameter == null
- && PropertyTarget != null
- && PropertyTarget.IsAlive
- && PropertyTarget.Target != null)
+ && _propertyTarget != null
+ && _propertyTarget.IsAlive
+ && _propertyTarget.Target != null)
{
- if (PropertyTarget.Target is INotifyPropertyChanged inpc2)
+ if (_propertyTarget.Target is INotifyPropertyChanged inpc2)
{
var listener = new PropertyChangedEventListener(this, inpc2, false);
_listeners.Add(listener);
@@ -1024,9 +1069,9 @@ private void Attach()
}
}
- private bool CanBeConverted(PropertyInfo sourceProperty, PropertyInfo targetProperty)
+ private bool CanBeConverted(PropertyInfo? sourceProperty, PropertyInfo? targetProperty)
{
- if (targetProperty == null)
+ if (sourceProperty == null || targetProperty == null)
{
return true;
}
@@ -1038,58 +1083,58 @@ private bool CanBeConverted(PropertyInfo sourceProperty, PropertyInfo targetProp
|| (IsValueType(sourceType) && IsValueType(targetType));
}
- private void DetachSourceHandlers()
+ private void DetachAllSourceHandlers()
{
- if (PropertySource == null
- || !PropertySource.IsAlive
- || PropertySource.Target == null)
+ if (_propertySource == null
+ || !_propertySource.IsAlive
+ || _propertySource.Target == null)
{
return;
}
foreach (var eventName in SourceHandlers.Keys)
{
- var type = PropertySource.Target.GetType();
- var ev = type.GetRuntimeEvent(eventName);
- if (ev == null)
+ var type = _propertySource.Target.GetType();
+ var @event = type.GetRuntimeEvent(eventName);
+ if (@event == null)
{
return;
}
- ev.RemoveEventHandler(PropertySource.Target, SourceHandlers[eventName].Delegate);
+ @event.RemoveEventHandler(_propertySource.Target, SourceHandlers[eventName].Delegate);
}
SourceHandlers.Clear();
}
- private void DetachTargetHandlers()
+ private void DetachAllTargetHandlers()
{
- if (PropertySource == null
- || !PropertySource.IsAlive
- || PropertySource.Target == null)
+ if (_propertySource == null
+ || !_propertySource.IsAlive
+ || _propertySource.Target == null)
{
return;
}
foreach (var eventName in TargetHandlers.Keys)
{
- var type = PropertyTarget.Target.GetType();
- var ev = type.GetRuntimeEvent(eventName);
- if (ev == null)
+ var type = _propertyTarget!.Target.GetType();
+ var @event = type.GetRuntimeEvent(eventName);
+ if (@event == null)
{
return;
}
- ev.RemoveEventHandler(PropertyTarget.Target, TargetHandlers[eventName].Delegate);
+ @event.RemoveEventHandler(_propertyTarget.Target, TargetHandlers[eventName].Delegate);
}
TargetHandlers.Clear();
}
- private static IList GetPropertyChain(
- object topInstance,
- IList