EventLoop V1
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using TerminalUI.Controls;
|
||||
using TerminalUI.Traits;
|
||||
@@ -8,41 +9,103 @@ namespace TerminalUI;
|
||||
public class Binding<TDataContext, TResult> : IDisposable
|
||||
{
|
||||
private readonly Func<TDataContext, TResult> _dataContextMapper;
|
||||
private IView<TDataContext> _view;
|
||||
private IView<TDataContext> _dataSourceView;
|
||||
private object? _propertySource;
|
||||
private PropertyInfo _targetProperty;
|
||||
private readonly List<string> _rerenderProperties;
|
||||
private readonly IDisposableCollection? _propertySourceDisposableCollection;
|
||||
private INotifyPropertyChanged? _dataSourceLastDataContext;
|
||||
|
||||
public Binding(
|
||||
IView<TDataContext> view,
|
||||
Func<TDataContext, TResult> dataContextMapper,
|
||||
object? propertySource,
|
||||
PropertyInfo targetProperty
|
||||
IView<TDataContext> dataSourceView,
|
||||
Expression<Func<TDataContext?, TResult>> dataContextExpression,
|
||||
object? propertySource,
|
||||
PropertyInfo targetProperty,
|
||||
IEnumerable<string>? rerenderProperties = null
|
||||
)
|
||||
{
|
||||
_view = view;
|
||||
_dataContextMapper = dataContextMapper;
|
||||
ArgumentNullException.ThrowIfNull(dataSourceView);
|
||||
ArgumentNullException.ThrowIfNull(dataContextExpression);
|
||||
ArgumentNullException.ThrowIfNull(targetProperty);
|
||||
_dataSourceView = dataSourceView;
|
||||
_dataContextMapper = dataContextExpression.Compile();
|
||||
_propertySource = propertySource;
|
||||
_targetProperty = targetProperty;
|
||||
view.PropertyChanged += View_PropertyChanged;
|
||||
_targetProperty.SetValue(_propertySource, _dataContextMapper(_view.DataContext));
|
||||
|
||||
if(propertySource is IDisposableCollection disposableCollection)
|
||||
disposableCollection.AddDisposable(this);
|
||||
|
||||
view.AddDisposable(this);
|
||||
_rerenderProperties = rerenderProperties?.ToList() ?? new List<string>();
|
||||
|
||||
FindReactiveProperties(dataContextExpression);
|
||||
|
||||
dataSourceView.PropertyChanged += View_PropertyChanged;
|
||||
var initialValue = _dataContextMapper(_dataSourceView.DataContext);
|
||||
_targetProperty.SetValue(_propertySource, initialValue);
|
||||
|
||||
if (propertySource is IDisposableCollection propertySourceDisposableCollection)
|
||||
{
|
||||
propertySourceDisposableCollection.AddDisposable(this);
|
||||
_propertySourceDisposableCollection = propertySourceDisposableCollection;
|
||||
}
|
||||
|
||||
if (_dataSourceView.DataContext is INotifyPropertyChanged dataSourcePropertyChanged)
|
||||
{
|
||||
_dataSourceLastDataContext = dataSourcePropertyChanged;
|
||||
dataSourcePropertyChanged.PropertyChanged += DataContext_PropertyChanged;
|
||||
}
|
||||
|
||||
dataSourceView.AddDisposable(this);
|
||||
}
|
||||
|
||||
private void FindReactiveProperties(Expression expression)
|
||||
{
|
||||
if (expression is LambdaExpression lambdaExpression)
|
||||
{
|
||||
FindReactiveProperties(lambdaExpression.Body);
|
||||
}
|
||||
else if (expression is ConditionalExpression conditionalExpression)
|
||||
{
|
||||
FindReactiveProperties(conditionalExpression.IfFalse);
|
||||
FindReactiveProperties(conditionalExpression.IfTrue);
|
||||
}
|
||||
else if (expression is MemberExpression {Member: PropertyInfo dataContextPropertyInfo})
|
||||
{
|
||||
_rerenderProperties.Add(dataContextPropertyInfo.Name);
|
||||
}
|
||||
//TODO: Handle other expression types
|
||||
}
|
||||
|
||||
private void View_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName != nameof(IView<TDataContext>.DataContext)) return;
|
||||
|
||||
_targetProperty.SetValue(_propertySource, _dataContextMapper(_view.DataContext));
|
||||
if (_dataSourceLastDataContext is not null)
|
||||
{
|
||||
_dataSourceLastDataContext.PropertyChanged -= DataContext_PropertyChanged;
|
||||
}
|
||||
|
||||
if (_dataSourceView.DataContext is INotifyPropertyChanged dataSourcePropertyChanged)
|
||||
{
|
||||
_dataSourceLastDataContext = dataSourcePropertyChanged;
|
||||
dataSourcePropertyChanged.PropertyChanged += DataContext_PropertyChanged;
|
||||
}
|
||||
|
||||
UpdateTargetProperty();
|
||||
}
|
||||
|
||||
private void DataContext_PropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == null
|
||||
|| !_rerenderProperties.Contains(e.PropertyName)) return;
|
||||
UpdateTargetProperty();
|
||||
}
|
||||
|
||||
private void UpdateTargetProperty()
|
||||
=> _targetProperty.SetValue(_propertySource, _dataContextMapper(_dataSourceView.DataContext));
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_view.PropertyChanged -= View_PropertyChanged;
|
||||
_view = null!;
|
||||
_propertySourceDisposableCollection?.RemoveDisposable(this);
|
||||
_dataSourceView.RemoveDisposable(this);
|
||||
_dataSourceView.PropertyChanged -= View_PropertyChanged;
|
||||
_dataSourceView = null!;
|
||||
_propertySource = null!;
|
||||
_targetProperty = null!;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user