DeclarativeProperty nullability fixes
This commit is contained in:
@@ -88,7 +88,7 @@ public record SizeScanContainer : ISizeScanContainer
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public required IDeclarativeProperty<string> Status { get; init; } = new DeclarativeProperty<string>();
|
public required IDeclarativeProperty<string> Status { get; init; } = new DeclarativeProperty<string>(string.Empty);
|
||||||
|
|
||||||
public required SizeScanTask SizeScanTask { get; init; }
|
public required SizeScanTask SizeScanTask { get; init; }
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class SizeScanTask : ISizeScanTask
|
|||||||
private readonly ILogger<SizeScanTask> _logger;
|
private readonly ILogger<SizeScanTask> _logger;
|
||||||
private Thread? _sizeScanThread;
|
private Thread? _sizeScanThread;
|
||||||
private static int _searchId = 1;
|
private static int _searchId = 1;
|
||||||
private readonly DeclarativeProperty<string> _containerStatus = new();
|
private readonly DeclarativeProperty<string> _containerStatus = new(string.Empty);
|
||||||
private readonly IDeclarativeProperty<string> _containerStatusDebounced;
|
private readonly IDeclarativeProperty<string> _containerStatusDebounced;
|
||||||
public ISizeScanContainer SizeSizeScanContainer { get; private set; } = null!;
|
public ISizeScanContainer SizeSizeScanContainer { get; private set; } = null!;
|
||||||
public bool IsRunning { get; private set; }
|
public bool IsRunning { get; private set; }
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace FileTime.App.Core.ViewModels;
|
|||||||
public abstract partial class AppStateBase : IAppState
|
public abstract partial class AppStateBase : IAppState
|
||||||
{
|
{
|
||||||
private readonly BehaviorSubject<string?> _searchText = new(null);
|
private readonly BehaviorSubject<string?> _searchText = new(null);
|
||||||
private readonly DeclarativeProperty<ITabViewModel?> _selectedTab = new();
|
private readonly DeclarativeProperty<ITabViewModel?> _selectedTab = new(null);
|
||||||
private readonly DeclarativeProperty<ViewMode> _viewMode = new(Models.Enums.ViewMode.Default);
|
private readonly DeclarativeProperty<ViewMode> _viewMode = new(Models.Enums.ViewMode.Default);
|
||||||
private readonly ObservableCollection<ITabViewModel> _tabs = new();
|
private readonly ObservableCollection<ITabViewModel> _tabs = new();
|
||||||
private readonly DeclarativeProperty<string?> _rapidTravelText;
|
private readonly DeclarativeProperty<string?> _rapidTravelText;
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ public class CopyCommand : CommandBase, ITransportationCommand
|
|||||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||||
|
|
||||||
private readonly List<OperationProgress> _operationProgresses = new();
|
private readonly List<OperationProgress> _operationProgresses = new();
|
||||||
private readonly DeclarativeProperty<OperationProgress?> _currentOperationProgress = new();
|
private readonly DeclarativeProperty<OperationProgress?> _currentOperationProgress = new(null);
|
||||||
|
|
||||||
private long _recentTotalSum;
|
private long _recentTotalSum;
|
||||||
private readonly DeclarativeProperty<long> _recentTotalProcessed = new();
|
private readonly DeclarativeProperty<long> _recentTotalProcessed = new(0);
|
||||||
private readonly DeclarativeProperty<DateTime> _recentStartTime = new();
|
private readonly DeclarativeProperty<DateTime> _recentStartTime = new(DateTime.Now);
|
||||||
|
|
||||||
public IReadOnlyList<FullName> Sources { get; }
|
public IReadOnlyList<FullName> Sources { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ public class Tab : ITab
|
|||||||
|
|
||||||
private readonly ITimelessContentProvider _timelessContentProvider;
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
private readonly ITabEvents _tabEvents;
|
private readonly ITabEvents _tabEvents;
|
||||||
private readonly DeclarativeProperty<IContainer?> _currentLocation = new();
|
private readonly DeclarativeProperty<IContainer?> _currentLocation = new(null);
|
||||||
private readonly DeclarativeProperty<IContainer?> _currentLocationForced = new();
|
private readonly DeclarativeProperty<IContainer?> _currentLocationForced = new(null);
|
||||||
private readonly DeclarativeProperty<AbsolutePath?> _currentRequestItem = new();
|
private readonly DeclarativeProperty<AbsolutePath?> _currentRequestItem = new(null);
|
||||||
private readonly ObservableCollection<ItemFilter> _itemFilters = new();
|
private readonly ObservableCollection<ItemFilter> _itemFilters = new();
|
||||||
private readonly CircularBuffer<FullName> _history = new(20);
|
private readonly CircularBuffer<FullName> _history = new(20);
|
||||||
private readonly CircularBuffer<FullName> _future = new(20);
|
private readonly CircularBuffer<FullName> _future = new(20);
|
||||||
@@ -64,28 +64,26 @@ public class Tab : ITab
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
|
|
||||||
CurrentItems = DeclarativePropertyHelpers.CombineLatest(
|
IDeclarativeProperty<ObservableCollection<IItem>?> asd1 = DeclarativePropertyHelpers.CombineLatest(
|
||||||
CurrentLocation,
|
CurrentLocation,
|
||||||
itemFiltersProperty,
|
itemFiltersProperty,
|
||||||
(container, filters) =>
|
Task<ObservableCollection<IItem>?> (container, filters) =>
|
||||||
{
|
{
|
||||||
ObservableCollection<IItem>? items = null;
|
ObservableCollection<IItem>? items = null;
|
||||||
|
|
||||||
if (container is not null)
|
if (container is null) return Task.FromResult(items);
|
||||||
{
|
|
||||||
items = container
|
|
||||||
.Items
|
|
||||||
.Selecting<AbsolutePath, IItem>(i => MapItem(i));
|
|
||||||
|
|
||||||
if (filters is not null)
|
items = container
|
||||||
{
|
.Items
|
||||||
items = items.Filtering(i => filters.All(f => f.Filter(i)));
|
.Selecting<AbsolutePath, IItem>(i => MapItem(i));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(items);
|
items = items.Filtering(i => filters.All(f => f.Filter(i)));
|
||||||
|
|
||||||
|
return Task.FromResult(items)!;
|
||||||
}
|
}
|
||||||
).CombineLatest(
|
);
|
||||||
|
|
||||||
|
var asd2 = asd1.CombineLatest(
|
||||||
Ordering.Map(ordering =>
|
Ordering.Map(ordering =>
|
||||||
{
|
{
|
||||||
var (itemComparer, order) = ordering switch
|
var (itemComparer, order) = ordering switch
|
||||||
@@ -103,7 +101,7 @@ public class Tab : ITab
|
|||||||
|
|
||||||
return (itemComparer, order, ordering is not ItemOrdering.Name and not ItemOrdering.NameDesc);
|
return (itemComparer, order, ordering is not ItemOrdering.Name and not ItemOrdering.NameDesc);
|
||||||
}),
|
}),
|
||||||
(items, ordering) =>
|
Task<ObservableCollection<IItem>?> (items, ordering) =>
|
||||||
{
|
{
|
||||||
if (items is null) return Task.FromResult<ObservableCollection<IItem>?>(null);
|
if (items is null) return Task.FromResult<ObservableCollection<IItem>?>(null);
|
||||||
|
|
||||||
@@ -117,14 +115,16 @@ public class Tab : ITab
|
|||||||
? primaryOrderedItems.ThenOrdering(i => i.DisplayName)
|
? primaryOrderedItems.ThenOrdering(i => i.DisplayName)
|
||||||
: primaryOrderedItems;
|
: primaryOrderedItems;
|
||||||
|
|
||||||
return Task.FromResult(orderedItems);
|
return Task.FromResult<ObservableCollection<IItem>?>(orderedItems);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CurrentItems = asd2;
|
||||||
|
|
||||||
CurrentSelectedItem = DeclarativePropertyHelpers.CombineLatest(
|
CurrentSelectedItem = DeclarativePropertyHelpers.CombineLatest(
|
||||||
CurrentItems.Watch<ObservableCollection<IItem>, IItem>(),
|
CurrentItems.Watch<ObservableCollection<IItem>?, IItem>(),
|
||||||
_currentRequestItem.DistinctUntilChanged(),
|
_currentRequestItem.DistinctUntilChanged(),
|
||||||
(items, selected) =>
|
Task<AbsolutePath?> (items, selected) =>
|
||||||
{
|
{
|
||||||
var itemSelectingContext = new LastItemSelectingContext(CurrentLocation.Value);
|
var itemSelectingContext = new LastItemSelectingContext(CurrentLocation.Value);
|
||||||
var lastItemSelectingContext = _lastItemSelectingContext;
|
var lastItemSelectingContext = _lastItemSelectingContext;
|
||||||
@@ -143,7 +143,7 @@ public class Tab : ITab
|
|||||||
var candidate = _selectedItemCandidates.FirstOrDefault(c => items.Any(i => i.FullName?.Path == c.Path.Path));
|
var candidate = _selectedItemCandidates.FirstOrDefault(c => items.Any(i => i.FullName?.Path == c.Path.Path));
|
||||||
if (candidate != null)
|
if (candidate != null)
|
||||||
{
|
{
|
||||||
return Task.FromResult(candidate);
|
return Task.FromResult<AbsolutePath?>(candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ public class Tab : ITab
|
|||||||
DeclarativePropertyHelpers.CombineLatest(
|
DeclarativePropertyHelpers.CombineLatest(
|
||||||
CurrentItems,
|
CurrentItems,
|
||||||
CurrentSelectedItem,
|
CurrentSelectedItem,
|
||||||
(items, selected) =>
|
Task<IEnumerable<AbsolutePath>?> (items, selected) =>
|
||||||
{
|
{
|
||||||
if (items is null || selected is null) return Task.FromResult<IEnumerable<AbsolutePath>?>(null);
|
if (items is null || selected is null) return Task.FromResult<IEnumerable<AbsolutePath>?>(null);
|
||||||
var primaryCandidates = items.SkipWhile(i => i.FullName is {Path: var p} && p != selected.Path.Path).Skip(1);
|
var primaryCandidates = items.SkipWhile(i => i.FullName is {Path: var p} && p != selected.Path.Path).Skip(1);
|
||||||
@@ -169,7 +169,7 @@ public class Tab : ITab
|
|||||||
.Concat(secondaryCandidates)
|
.Concat(secondaryCandidates)
|
||||||
.Select(c => new AbsolutePath(_timelessContentProvider, c));
|
.Select(c => new AbsolutePath(_timelessContentProvider, c));
|
||||||
|
|
||||||
return Task.FromResult(candidates);
|
return Task.FromResult<IEnumerable<AbsolutePath>?>(candidates);
|
||||||
})
|
})
|
||||||
.Subscribe(candidates =>
|
.Subscribe(candidates =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ public interface IMainWindowViewModelBase
|
|||||||
DeclarativeProperty<string> Title { get; }
|
DeclarativeProperty<string> Title { get; }
|
||||||
bool Loading { get; }
|
bool Loading { get; }
|
||||||
IObservable<string?> MainFont { get; }
|
IObservable<string?> MainFont { get; }
|
||||||
DeclarativeProperty<string> FatalError { get; }
|
DeclarativeProperty<string?> FatalError { get; }
|
||||||
IReadOnlyList<WindowTransparencyLevel> TransparencyLevelHint { get; }
|
IReadOnlyList<WindowTransparencyLevel> TransparencyLevelHint { get; }
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,6 @@ public class MainWindowLoadingViewModel : IMainWindowViewModelBase
|
|||||||
public bool Loading => true;
|
public bool Loading => true;
|
||||||
public IObservable<string?> MainFont { get; } = new BehaviorSubject<string?>("");
|
public IObservable<string?> MainFont { get; } = new BehaviorSubject<string?>("");
|
||||||
public DeclarativeProperty<string> Title { get; } = new("Loading...");
|
public DeclarativeProperty<string> Title { get; } = new("Loading...");
|
||||||
public DeclarativeProperty<string> FatalError { get; } = new();
|
public DeclarativeProperty<string?> FatalError { get; } = new(null);
|
||||||
public IReadOnlyList<WindowTransparencyLevel> TransparencyLevelHint { get; } = new[] {WindowTransparencyLevel.AcrylicBlur};
|
public IReadOnlyList<WindowTransparencyLevel> TransparencyLevelHint { get; } = new[] {WindowTransparencyLevel.AcrylicBlur};
|
||||||
}
|
}
|
||||||
@@ -53,10 +53,10 @@ public partial class MainWindowViewModel : IMainWindowViewModel
|
|||||||
private readonly OcConsumer _rootDriveInfosConsumer = new();
|
private readonly OcConsumer _rootDriveInfosConsumer = new();
|
||||||
public bool Loading => false;
|
public bool Loading => false;
|
||||||
public IObservable<string?> MainFont => _fontService.MainFont.Select(x => x ?? "");
|
public IObservable<string?> MainFont => _fontService.MainFont.Select(x => x ?? "");
|
||||||
public DeclarativeProperty<string> FatalError { get; } = new();
|
public DeclarativeProperty<string?> FatalError { get; } = new(null);
|
||||||
public IReadOnlyList<WindowTransparencyLevel> TransparencyLevelHint { get; } = new[] {WindowTransparencyLevel.Blur};
|
public IReadOnlyList<WindowTransparencyLevel> TransparencyLevelHint { get; } = new[] {WindowTransparencyLevel.Blur};
|
||||||
public IGuiAppState AppState => _appState;
|
public IGuiAppState AppState => _appState;
|
||||||
public DeclarativeProperty<string> Title { get; } = new();
|
public DeclarativeProperty<string> Title { get; } = new(string.Empty);
|
||||||
public Thickness IconStatusPanelMargin { get; private set; } = new(20, 10, 10, 10);
|
public Thickness IconStatusPanelMargin { get; private set; } = new(20, 10, 10, 10);
|
||||||
public Action? FocusDefaultElement { get; set; }
|
public Action? FocusDefaultElement { get; set; }
|
||||||
public Action? ShowWindow { get; set; }
|
public Action? ShowWindow { get; set; }
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
namespace DeclarativeProperty;
|
namespace DeclarativeProperty;
|
||||||
|
|
||||||
public class CollectionRepeaterProperty<TCollection, TItem> : DeclarativePropertyBase<TCollection>
|
public class CollectionRepeaterProperty<TCollection, TItem> : DeclarativePropertyBase<TCollection>
|
||||||
where TCollection : IList<TItem>, INotifyCollectionChanged
|
where TCollection : IList<TItem>?, INotifyCollectionChanged?
|
||||||
{
|
{
|
||||||
private TCollection? _currentCollection;
|
private TCollection? _currentCollection;
|
||||||
|
|
||||||
public CollectionRepeaterProperty(IDeclarativeProperty<TCollection?> from) : base(from.Value)
|
public CollectionRepeaterProperty(IDeclarativeProperty<TCollection> from) : base(from.Value)
|
||||||
{
|
{
|
||||||
_currentCollection = from.Value;
|
_currentCollection = from.Value;
|
||||||
if (from.Value is { } value)
|
if (from.Value is { } value)
|
||||||
@@ -18,7 +18,7 @@ public class CollectionRepeaterProperty<TCollection, TItem> : DeclarativePropert
|
|||||||
AddDisposable(from.Subscribe(Handle));
|
AddDisposable(from.Subscribe(Handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CollectionRepeaterProperty(TCollection? collection) : base(collection)
|
public CollectionRepeaterProperty(TCollection collection) : base(collection)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(collection);
|
ArgumentNullException.ThrowIfNull(collection);
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ public class CollectionRepeaterProperty<TCollection, TItem> : DeclarativePropert
|
|||||||
t.Wait();
|
t.Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Handle(TCollection? collection, CancellationToken cancellationToken = default)
|
private async Task Handle(TCollection collection, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (_currentCollection is { } currentCollection)
|
if (_currentCollection is { } currentCollection)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,18 +3,18 @@
|
|||||||
public class CombineAllProperty<T, TResult> : DeclarativePropertyBase<TResult>
|
public class CombineAllProperty<T, TResult> : DeclarativePropertyBase<TResult>
|
||||||
{
|
{
|
||||||
private readonly List<IDeclarativeProperty<T>> _sources;
|
private readonly List<IDeclarativeProperty<T>> _sources;
|
||||||
private readonly Func<IEnumerable<T>, Task<TResult?>> _combiner;
|
private readonly Func<IEnumerable<T>, Task<TResult>> _combiner;
|
||||||
|
|
||||||
public CombineAllProperty(
|
public CombineAllProperty(
|
||||||
IEnumerable<IDeclarativeProperty<T>> sources,
|
IEnumerable<IDeclarativeProperty<T>> sources,
|
||||||
Func<IEnumerable<T>, Task<TResult?>> combiner,
|
Func<IEnumerable<T>, Task<TResult>> combiner,
|
||||||
Action<TResult?>? setValueHook = null) : base(setValueHook)
|
Action<TResult>? setValueHook = null) : base(default!, setValueHook)
|
||||||
{
|
{
|
||||||
var sourcesList = sources.ToList();
|
var sourcesList = sources.ToList();
|
||||||
_sources = sourcesList;
|
_sources = sourcesList;
|
||||||
_combiner = combiner;
|
_combiner = combiner;
|
||||||
|
|
||||||
var initialValueTask = _combiner(sourcesList.Select(p => p.Value)!);
|
var initialValueTask = Task.Run(async () => await _combiner(sourcesList.Select(p => p.Value)));
|
||||||
initialValueTask.Wait();
|
initialValueTask.Wait();
|
||||||
SetNewValueSync(initialValueTask.Result);
|
SetNewValueSync(initialValueTask.Result);
|
||||||
|
|
||||||
@@ -24,12 +24,12 @@ public class CombineAllProperty<T, TResult> : DeclarativePropertyBase<TResult>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnSourceChanged(T? arg1, CancellationToken arg2) => await Update();
|
private async Task OnSourceChanged(T arg1, CancellationToken arg2) => await Update();
|
||||||
|
|
||||||
private async Task Update()
|
private async Task Update()
|
||||||
{
|
{
|
||||||
var values = _sources.Select(p => p.Value);
|
var values = _sources.Select(p => p.Value);
|
||||||
var result = await _combiner(values!);
|
var result = await _combiner(values);
|
||||||
|
|
||||||
await SetNewValueAsync(result);
|
await SetNewValueAsync(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,27 @@
|
|||||||
|
|
||||||
public sealed class CombineLatestProperty<T1, T2, TResult> : DeclarativePropertyBase<TResult>
|
public sealed class CombineLatestProperty<T1, T2, TResult> : DeclarativePropertyBase<TResult>
|
||||||
{
|
{
|
||||||
private readonly Func<T1?, T2?, Task<TResult>> _func;
|
private readonly Func<T1, T2, Task<TResult>> _func;
|
||||||
private T1? _value1;
|
private T1 _value1;
|
||||||
private T2? _value2;
|
private T2 _value2;
|
||||||
|
|
||||||
public CombineLatestProperty(
|
public CombineLatestProperty(
|
||||||
IDeclarativeProperty<T1> prop1,
|
IDeclarativeProperty<T1> prop1,
|
||||||
IDeclarativeProperty<T2> prop2,
|
IDeclarativeProperty<T2> prop2,
|
||||||
Func<T1?, T2?, Task<TResult>> func,
|
Func<T1, T2, Task<TResult>> func,
|
||||||
Action<TResult?>? setValueHook = null) : base(setValueHook)
|
Action<TResult>? setValueHook = null) : base(default!, setValueHook)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(prop1);
|
ArgumentNullException.ThrowIfNull(prop1);
|
||||||
ArgumentNullException.ThrowIfNull(prop2);
|
ArgumentNullException.ThrowIfNull(prop2);
|
||||||
|
|
||||||
_func = func;
|
_func = func;
|
||||||
|
|
||||||
_value1 = prop1.Value is null ? default : prop1.Value;
|
_value1 = prop1.Value;
|
||||||
_value2 = prop2.Value is null ? default : prop2.Value;
|
_value2 = prop2.Value;
|
||||||
|
|
||||||
|
var initialValueTask = Task.Run(async () => await _func(_value1, _value2));
|
||||||
|
initialValueTask.Wait();
|
||||||
|
SetNewValueSync(initialValueTask.Result);
|
||||||
|
|
||||||
prop1.Subscribe(async (value1, token) =>
|
prop1.Subscribe(async (value1, token) =>
|
||||||
{
|
{
|
||||||
@@ -51,7 +55,7 @@ public sealed class CombineLatestProperty<T1, T2, T3, TResult> : DeclarativeProp
|
|||||||
IDeclarativeProperty<T2> prop2,
|
IDeclarativeProperty<T2> prop2,
|
||||||
IDeclarativeProperty<T3> prop3,
|
IDeclarativeProperty<T3> prop3,
|
||||||
Func<T1?, T2?, T3?,Task<TResult>> func,
|
Func<T1?, T2?, T3?,Task<TResult>> func,
|
||||||
Action<TResult?>? setValueHook = null) : base(setValueHook)
|
Action<TResult>? setValueHook = null) : base(default!, setValueHook)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(prop1);
|
ArgumentNullException.ThrowIfNull(prop1);
|
||||||
ArgumentNullException.ThrowIfNull(prop2);
|
ArgumentNullException.ThrowIfNull(prop2);
|
||||||
@@ -59,9 +63,13 @@ public sealed class CombineLatestProperty<T1, T2, T3, TResult> : DeclarativeProp
|
|||||||
|
|
||||||
_func = func;
|
_func = func;
|
||||||
|
|
||||||
_value1 = prop1.Value is null ? default : prop1.Value;
|
_value1 = prop1.Value;
|
||||||
_value2 = prop2.Value is null ? default : prop2.Value;
|
_value2 = prop2.Value;
|
||||||
_value3 = prop3.Value is null ? default : prop3.Value;
|
_value3 = prop3.Value;
|
||||||
|
|
||||||
|
var initialValueTask = Task.Run(async () => await _func(_value1, _value2, _value3));
|
||||||
|
initialValueTask.Wait();
|
||||||
|
SetNewValueSync(initialValueTask.Result);
|
||||||
|
|
||||||
prop1.Subscribe(async (value1, token) =>
|
prop1.Subscribe(async (value1, token) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ public sealed class CombineProperty<TFrom, TTo> : DeclarativePropertyBase<TTo>
|
|||||||
private readonly Func<IReadOnlyList<TFrom?>, Task<TTo>> _combiner;
|
private readonly Func<IReadOnlyList<TFrom?>, Task<TTo>> _combiner;
|
||||||
private readonly List<IDeclarativeProperty<TFrom>> _sourceProperties = new();
|
private readonly List<IDeclarativeProperty<TFrom>> _sourceProperties = new();
|
||||||
|
|
||||||
public CombineProperty(Func<IReadOnlyList<TFrom?>, Task<TTo>> combiner)
|
public CombineProperty(Func<IReadOnlyList<TFrom?>, Task<TTo>> combiner) : base(default!)
|
||||||
{
|
{
|
||||||
_combiner = combiner;
|
_combiner = combiner;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
public sealed class DebounceProperty<T> : DeclarativePropertyBase<T>
|
public sealed class DebounceProperty<T> : DeclarativePropertyBase<T>
|
||||||
{
|
{
|
||||||
private readonly object _lock = new();
|
private readonly object _lock = new();
|
||||||
private readonly Func<T?, TimeSpan> _interval;
|
private readonly Func<T, TimeSpan> _interval;
|
||||||
private DateTime _startTime = DateTime.MinValue;
|
private DateTime _startTime = DateTime.MinValue;
|
||||||
private T? _nextValue;
|
private T _nextValue = default!;
|
||||||
private CancellationToken _nextCancellationToken;
|
private CancellationToken _nextCancellationToken;
|
||||||
private bool _isThrottleTaskRunning;
|
private bool _isThrottleTaskRunning;
|
||||||
public bool ResetTimer { get; init; }
|
public bool ResetTimer { get; init; }
|
||||||
@@ -13,14 +13,14 @@ public sealed class DebounceProperty<T> : DeclarativePropertyBase<T>
|
|||||||
|
|
||||||
public DebounceProperty(
|
public DebounceProperty(
|
||||||
IDeclarativeProperty<T> from,
|
IDeclarativeProperty<T> from,
|
||||||
Func<T?, TimeSpan> interval,
|
Func<T, TimeSpan> interval,
|
||||||
Action<T?>? setValueHook = null) : base(from.Value, setValueHook)
|
Action<T>? setValueHook = null) : base(from.Value, setValueHook)
|
||||||
{
|
{
|
||||||
_interval = interval;
|
_interval = interval;
|
||||||
AddDisposable(from.Subscribe(SetValue));
|
AddDisposable(from.Subscribe(SetValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task SetValue(T? next, CancellationToken cancellationToken = default)
|
private Task SetValue(T next, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -38,7 +38,7 @@ public sealed class DebounceProperty<T> : DeclarativePropertyBase<T>
|
|||||||
|
|
||||||
_startTime = DateTime.Now;
|
_startTime = DateTime.Now;
|
||||||
_isThrottleTaskRunning = true;
|
_isThrottleTaskRunning = true;
|
||||||
Task.Run(async () => await StartDebounceTask());
|
Task.Run(StartDebounceTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@@ -51,7 +51,7 @@ public sealed class DebounceProperty<T> : DeclarativePropertyBase<T>
|
|||||||
await Task.Delay(WaitInterval);
|
await Task.Delay(WaitInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
T? next;
|
T next;
|
||||||
CancellationToken cancellationToken;
|
CancellationToken cancellationToken;
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ public static class DeclarativePropertyHelpers
|
|||||||
IDeclarativeProperty<T1> prop1,
|
IDeclarativeProperty<T1> prop1,
|
||||||
IDeclarativeProperty<T2> prop2,
|
IDeclarativeProperty<T2> prop2,
|
||||||
Func<T1, T2, Task<TResult>> func,
|
Func<T1, T2, Task<TResult>> func,
|
||||||
Action<TResult?>? setValueHook = null)
|
Action<TResult>? setValueHook = null)
|
||||||
=> new(prop1, prop2, func!, setValueHook);
|
=> new(prop1, prop2, func, setValueHook);
|
||||||
|
|
||||||
public static CombineLatestProperty<T1, T2, T3, TResult> CombineLatest<T1, T2, T3, TResult>(
|
public static CombineLatestProperty<T1, T2, T3, TResult> CombineLatest<T1, T2, T3, TResult>(
|
||||||
IDeclarativeProperty<T1> prop1,
|
IDeclarativeProperty<T1> prop1,
|
||||||
IDeclarativeProperty<T2> prop2,
|
IDeclarativeProperty<T2> prop2,
|
||||||
IDeclarativeProperty<T3> prop3,
|
IDeclarativeProperty<T3> prop3,
|
||||||
Func<T1, T2, T3, Task<TResult>> func,
|
Func<T1, T2, T3, Task<TResult>> func,
|
||||||
Action<TResult?>? setValueHook = null)
|
Action<TResult>? setValueHook = null)
|
||||||
=> new(prop1, prop2, prop3, func!, setValueHook);
|
=> new(prop1, prop2, prop3, func!, setValueHook);
|
||||||
|
|
||||||
public static MergeProperty<T> Merge<T>(params IDeclarativeProperty<T>[] props)
|
public static MergeProperty<T> Merge<T>(params IDeclarativeProperty<T>[] props)
|
||||||
@@ -23,11 +23,7 @@ public static class DeclarativePropertyHelpers
|
|||||||
|
|
||||||
public sealed class DeclarativeProperty<T> : DeclarativePropertyBase<T>
|
public sealed class DeclarativeProperty<T> : DeclarativePropertyBase<T>
|
||||||
{
|
{
|
||||||
public DeclarativeProperty(Action<T?>? setValueHook = null) : base(setValueHook)
|
public DeclarativeProperty(T initialValue, Action<T>? setValueHook = null) : base(initialValue, setValueHook)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public DeclarativeProperty(T initialValue, Action<T?>? setValueHook = null) : base(initialValue, setValueHook)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ namespace DeclarativeProperty;
|
|||||||
|
|
||||||
public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
||||||
{
|
{
|
||||||
private readonly List<Func<T?, CancellationToken, Task>> _subscribers = new();
|
private readonly List<Func<T, CancellationToken, Task>> _subscribers = new();
|
||||||
private readonly Action<T?>? _setValueHook;
|
private readonly Action<T>? _setValueHook;
|
||||||
private readonly List<IDisposable> _disposables = new();
|
private readonly List<IDisposable> _disposables = new();
|
||||||
private readonly List<Func<IDeclarativeProperty<T>, T, IDisposable?>> _subscribeTriggers = new();
|
private readonly List<Func<IDeclarativeProperty<T>, T, IDisposable?>> _subscribeTriggers = new();
|
||||||
private readonly List<Action<IDeclarativeProperty<T>, T>> _unsubscribeTriggers = new();
|
private readonly List<Action<IDeclarativeProperty<T>, T>> _unsubscribeTriggers = new();
|
||||||
@@ -14,29 +14,24 @@ public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
|||||||
private readonly object _triggerLock = new();
|
private readonly object _triggerLock = new();
|
||||||
private readonly object _subscriberLock = new();
|
private readonly object _subscriberLock = new();
|
||||||
|
|
||||||
private T? _value;
|
private T _value;
|
||||||
|
|
||||||
public T? Value
|
public T Value
|
||||||
{
|
{
|
||||||
get => _value;
|
get => _value;
|
||||||
set => _setValueHook?.Invoke(value);
|
set => _setValueHook?.Invoke(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DeclarativePropertyBase(Action<T?>? setValueHook = null)
|
protected DeclarativePropertyBase(T initialValue, Action<T>? setValueHook = null)
|
||||||
{
|
|
||||||
_setValueHook = setValueHook;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected DeclarativePropertyBase(T? initialValue, Action<T?>? setValueHook = null)
|
|
||||||
{
|
{
|
||||||
_setValueHook = setValueHook;
|
_setValueHook = setValueHook;
|
||||||
_value = initialValue;
|
_value = initialValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected async Task NotifySubscribersAsync(T? newValue, CancellationToken cancellationToken = default)
|
protected async Task NotifySubscribersAsync(T newValue, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
List<Func<T?, CancellationToken, Task>> subscribers;
|
List<Func<T, CancellationToken, Task>> subscribers;
|
||||||
lock (_subscriberLock)
|
lock (_subscriberLock)
|
||||||
{
|
{
|
||||||
subscribers = _subscribers.ToList();
|
subscribers = _subscribers.ToList();
|
||||||
@@ -54,7 +49,7 @@ public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
|||||||
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||||
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
|
||||||
public IDisposable Subscribe(Func<T?, CancellationToken, Task> onChange)
|
public IDisposable Subscribe(Func<T, CancellationToken, Task> onChange)
|
||||||
{
|
{
|
||||||
lock (_subscriberLock)
|
lock (_subscriberLock)
|
||||||
{
|
{
|
||||||
@@ -70,7 +65,7 @@ public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
|||||||
{
|
{
|
||||||
lock (_subscriberLock)
|
lock (_subscriberLock)
|
||||||
{
|
{
|
||||||
_subscribers.Remove(onChange!);
|
_subscribers.Remove(onChange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,14 +94,14 @@ public abstract class DeclarativePropertyBase<T> : IDeclarativeProperty<T>
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
|
|
||||||
protected async Task SetNewValueAsync(T? newValue, CancellationToken cancellationToken = default)
|
protected async Task SetNewValueAsync(T newValue, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
SetNewValueSync(newValue, cancellationToken);
|
SetNewValueSync(newValue, cancellationToken);
|
||||||
if (cancellationToken.IsCancellationRequested) return;
|
if (cancellationToken.IsCancellationRequested) return;
|
||||||
await NotifySubscribersAsync(newValue, cancellationToken);
|
await NotifySubscribersAsync(newValue, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SetNewValueSync(T? newValue, CancellationToken cancellationToken = default)
|
protected void SetNewValueSync(T newValue, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (!(Value?.Equals(newValue) ?? false))
|
if (!(Value?.Equals(newValue) ?? false))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,16 +21,16 @@ public static class DeclarativePropertyExtensions
|
|||||||
public static IDeclarativeProperty<T> DistinctUntilChanged<T>(this IDeclarativeProperty<T> from)
|
public static IDeclarativeProperty<T> DistinctUntilChanged<T>(this IDeclarativeProperty<T> from)
|
||||||
=> new DistinctUntilChangedProperty<T>(from);
|
=> new DistinctUntilChangedProperty<T>(from);
|
||||||
|
|
||||||
public static IDeclarativeProperty<TTo> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, Task<TTo?>> mapper)
|
public static IDeclarativeProperty<TTo> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom> from, Func<TFrom, Task<TTo>> mapper)
|
||||||
=> Map(from, async (v, _) => await mapper(v));
|
=> Map(from, async (v, _) => await mapper(v));
|
||||||
|
|
||||||
public static IDeclarativeProperty<TTo> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, CancellationToken, Task<TTo?>> mapper)
|
public static IDeclarativeProperty<TTo> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom> from, Func<TFrom, CancellationToken, Task<TTo>> mapper)
|
||||||
=> new MapProperty<TFrom?, TTo>(mapper, from);
|
=> new MapProperty<TFrom, TTo>(mapper, from);
|
||||||
|
|
||||||
public static IDeclarativeProperty<TTo?> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, TTo?> mapper)
|
public static IDeclarativeProperty<TTo> Map<TFrom, TTo>(this IDeclarativeProperty<TFrom> from, Func<TFrom, TTo> mapper)
|
||||||
=> new MapProperty<TFrom?, TTo?>((next, _) => Task.FromResult(mapper(next)), from);
|
=> new MapProperty<TFrom, TTo>((next, _) => Task.FromResult(mapper(next)), from);
|
||||||
|
|
||||||
public static async Task<IDeclarativeProperty<TTo>> MapAsync<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, CancellationToken, Task<TTo?>> mapper)
|
public static async Task<IDeclarativeProperty<TTo>> MapAsync<TFrom, TTo>(this IDeclarativeProperty<TFrom> from, Func<TFrom, CancellationToken, Task<TTo>> mapper)
|
||||||
=> await MapProperty<TFrom, TTo>.CreateAsync(mapper, from);
|
=> await MapProperty<TFrom, TTo>.CreateAsync(mapper, from);
|
||||||
|
|
||||||
public static async Task<IDeclarativeProperty<TTo?>> MapAsync<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, TTo?> mapper)
|
public static async Task<IDeclarativeProperty<TTo?>> MapAsync<TFrom, TTo>(this IDeclarativeProperty<TFrom?> from, Func<TFrom?, TTo?> mapper)
|
||||||
@@ -43,39 +43,39 @@ public static class DeclarativePropertyExtensions
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
|
|
||||||
public static IDisposable Subscribe<T>(this IDeclarativeProperty<T> property, Action<T?> onChange)
|
public static IDisposable Subscribe<T>(this IDeclarativeProperty<T> property, Action<T> onChange)
|
||||||
=> property.Subscribe((value, _) =>
|
=> property.Subscribe((value, _) =>
|
||||||
{
|
{
|
||||||
onChange(value);
|
onChange(value);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
|
|
||||||
public static IDeclarativeProperty<T> Extract<T>(
|
public static IDeclarativeProperty<TResult> Extract<T, TResult>(
|
||||||
this IDeclarativeProperty<ReadOnlyObservableCollection<T>> from,
|
this IDeclarativeProperty<ReadOnlyObservableCollection<T>?> from,
|
||||||
Func<IList<T>?, T?> extractor
|
Func<IList<T>?, TResult> extractor
|
||||||
)
|
)
|
||||||
=> new ExtractorProperty<T>(from, extractor);
|
=> new ExtractorProperty<T, TResult>(from, extractor);
|
||||||
|
|
||||||
public static IDeclarativeProperty<T> Extract<T>(
|
public static IDeclarativeProperty<TResult> Extract<T, TResult>(
|
||||||
this IDeclarativeProperty<ObservableCollection<T>> from,
|
this IDeclarativeProperty<ObservableCollection<T>?> from,
|
||||||
Func<IList<T>?, T?> extractor
|
Func<IList<T>?, TResult> extractor
|
||||||
)
|
)
|
||||||
=> new ExtractorProperty<T>(from, extractor);
|
=> new ExtractorProperty<T, TResult>(from, extractor);
|
||||||
|
|
||||||
public static IDeclarativeProperty<T> Extract<T>(
|
public static IDeclarativeProperty<TResult> Extract<T, TResult>(
|
||||||
this IDeclarativeProperty<CollectionComputing<T>> from,
|
this IDeclarativeProperty<CollectionComputing<T>?> from,
|
||||||
Func<IList<T>?, T?> extractor
|
Func<IList<T>?, TResult> extractor
|
||||||
)
|
)
|
||||||
=> new ExtractorProperty<T>(from, extractor);
|
=> new ExtractorProperty<T, TResult>(from, extractor);
|
||||||
|
|
||||||
public static IDeclarativeProperty<TCollection> Watch<TCollection, TItem>(
|
public static IDeclarativeProperty<TCollection> Watch<TCollection, TItem>(
|
||||||
this IDeclarativeProperty<TCollection?> collection)
|
this IDeclarativeProperty<TCollection> collection)
|
||||||
where TCollection : IList<TItem>, INotifyCollectionChanged
|
where TCollection : IList<TItem>?, INotifyCollectionChanged?
|
||||||
=> new CollectionRepeaterProperty<TCollection, TItem>(collection);
|
=> new CollectionRepeaterProperty<TCollection, TItem>(collection);
|
||||||
|
|
||||||
public static IDeclarativeProperty<TCollection> Watch<TCollection, TItem>(
|
public static IDeclarativeProperty<TCollection> Watch<TCollection, TItem>(
|
||||||
this TCollection collection)
|
this TCollection collection)
|
||||||
where TCollection : IList<TItem>, INotifyCollectionChanged
|
where TCollection : IList<TItem>?, INotifyCollectionChanged?
|
||||||
=> new CollectionRepeaterProperty<TCollection, TItem>(collection);
|
=> new CollectionRepeaterProperty<TCollection, TItem>(collection);
|
||||||
|
|
||||||
public static IDeclarativeProperty<ObservableCollection<TItem>> Watch<TItem>(
|
public static IDeclarativeProperty<ObservableCollection<TItem>> Watch<TItem>(
|
||||||
@@ -90,16 +90,16 @@ public static class DeclarativePropertyExtensions
|
|||||||
public static IDeclarativeProperty<TResult> CombineLatest<T1, T2, TResult>(
|
public static IDeclarativeProperty<TResult> CombineLatest<T1, T2, TResult>(
|
||||||
this IDeclarativeProperty<T1> prop1,
|
this IDeclarativeProperty<T1> prop1,
|
||||||
IDeclarativeProperty<T2> prop2,
|
IDeclarativeProperty<T2> prop2,
|
||||||
Func<T1, T2, Task<TResult?>> func,
|
Func<T1, T2, Task<TResult>> func,
|
||||||
Action<TResult?>? setValueHook = null)
|
Action<TResult?>? setValueHook = null)
|
||||||
=> new CombineLatestProperty<T1,T2,TResult>(prop1, prop2, func!, setValueHook);
|
=> new CombineLatestProperty<T1,T2,TResult>(prop1, prop2, func, setValueHook);
|
||||||
|
|
||||||
public static IDeclarativeProperty<T?> Switch<T>(this IDeclarativeProperty<IDeclarativeProperty<T?>?> from)
|
public static IDeclarativeProperty<T> Switch<T>(this IDeclarativeProperty<IDeclarativeProperty<T>?> from)
|
||||||
=> new SwitchProperty<T?>(from);
|
=> new SwitchProperty<T>(from);
|
||||||
|
|
||||||
public static IDeclarativeProperty<TResult> CombineAll<T, TResult>(
|
public static IDeclarativeProperty<TResult> CombineAll<T, TResult>(
|
||||||
this IEnumerable<IDeclarativeProperty<T>> sources,
|
this IEnumerable<IDeclarativeProperty<T>> sources,
|
||||||
Func<IEnumerable<T>, Task<TResult?>> combiner,
|
Func<IEnumerable<T>, Task<TResult>> combiner,
|
||||||
Action<TResult?>? setValueHook = null)
|
Action<TResult?>? setValueHook = null)
|
||||||
=> new CombineAllProperty<T,TResult>(sources, combiner, setValueHook);
|
=> new CombineAllProperty<T,TResult>(sources, combiner, setValueHook);
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ public sealed class DistinctUntilChangedProperty<T> : DeclarativePropertyBase<T>
|
|||||||
AddDisposable(from.Subscribe(Handle));
|
AddDisposable(from.Subscribe(Handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task Handle(T? next, CancellationToken cancellationToken = default)
|
async Task Handle(T next, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (_comparer is { } comparer)
|
if (_comparer is { } comparer)
|
||||||
{
|
{
|
||||||
@@ -20,8 +20,10 @@ public sealed class DistinctUntilChangedProperty<T> : DeclarativePropertyBase<T>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((next is null && Value is null && !_firstFire)
|
else if (
|
||||||
|| (Value?.Equals(next) ?? false))
|
(next is null && Value is null && !_firstFire)
|
||||||
|
|| (Value?.Equals(next) ?? false)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using ObservableComputations;
|
|||||||
|
|
||||||
namespace DeclarativeProperty;
|
namespace DeclarativeProperty;
|
||||||
|
|
||||||
public sealed class ExtractorProperty<T> : DeclarativePropertyBase<T>
|
public sealed class ExtractorProperty<T, TResult> : DeclarativePropertyBase<TResult>
|
||||||
{
|
{
|
||||||
private interface ICollectionWrapper : IDisposable
|
private interface ICollectionWrapper : IDisposable
|
||||||
{
|
{
|
||||||
@@ -108,12 +108,12 @@ public sealed class ExtractorProperty<T> : DeclarativePropertyBase<T>
|
|||||||
) => new CollectionComputingWrapper(collection, handler);
|
) => new CollectionComputingWrapper(collection, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Func<IList<T>?, T?> _extractor;
|
private readonly Func<IList<T>?, TResult> _extractor;
|
||||||
private ICollectionWrapper? _collectionWrapper;
|
private ICollectionWrapper? _collectionWrapper;
|
||||||
|
|
||||||
public ExtractorProperty(
|
public ExtractorProperty(
|
||||||
IDeclarativeProperty<ObservableCollection<T>> from,
|
IDeclarativeProperty<ObservableCollection<T>?> from,
|
||||||
Func<IList<T>?, T?> extractor) : base(extractor(from.Value))
|
Func<IList<T>?, TResult> extractor) : base(extractor(from.Value))
|
||||||
{
|
{
|
||||||
_extractor = extractor;
|
_extractor = extractor;
|
||||||
_collectionWrapper = from.Value is null
|
_collectionWrapper = from.Value is null
|
||||||
@@ -124,8 +124,8 @@ public sealed class ExtractorProperty<T> : DeclarativePropertyBase<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ExtractorProperty(
|
public ExtractorProperty(
|
||||||
IDeclarativeProperty<ReadOnlyObservableCollection<T>> from,
|
IDeclarativeProperty<ReadOnlyObservableCollection<T>?> from,
|
||||||
Func<IList<T>?, T?> extractor) : base(extractor(from.Value))
|
Func<IList<T>?, TResult> extractor) : base(extractor(from.Value))
|
||||||
{
|
{
|
||||||
_extractor = extractor;
|
_extractor = extractor;
|
||||||
_collectionWrapper = from.Value is null
|
_collectionWrapper = from.Value is null
|
||||||
@@ -136,8 +136,8 @@ public sealed class ExtractorProperty<T> : DeclarativePropertyBase<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ExtractorProperty(
|
public ExtractorProperty(
|
||||||
IDeclarativeProperty<CollectionComputing<T>> from,
|
IDeclarativeProperty<CollectionComputing<T>?> from,
|
||||||
Func<IList<T>?, T?> extractor) : base(extractor(from.Value))
|
Func<IList<T>?, TResult> extractor) : base(extractor(from.Value))
|
||||||
{
|
{
|
||||||
_extractor = extractor;
|
_extractor = extractor;
|
||||||
_collectionWrapper = from.Value is null
|
_collectionWrapper = from.Value is null
|
||||||
@@ -165,9 +165,7 @@ public sealed class ExtractorProperty<T> : DeclarativePropertyBase<T>
|
|||||||
|
|
||||||
private async Task Fire(IList<T>? items, CancellationToken cancellationToken = default)
|
private async Task Fire(IList<T>? items, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var newValue = items is null
|
var newValue = _extractor(items);
|
||||||
? default
|
|
||||||
: _extractor(items);
|
|
||||||
|
|
||||||
await SetNewValueAsync(newValue, cancellationToken);
|
await SetNewValueAsync(newValue, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,24 +7,33 @@ public sealed class FilterProperty<T> : DeclarativePropertyBase<T>
|
|||||||
public FilterProperty(
|
public FilterProperty(
|
||||||
Func<T?, Task<bool>> filter,
|
Func<T?, Task<bool>> filter,
|
||||||
IDeclarativeProperty<T> from,
|
IDeclarativeProperty<T> from,
|
||||||
Action<T?>? setValueHook = null) : base(setValueHook)
|
Action<T>? setValueHook = null) : base(default!, setValueHook)
|
||||||
{
|
{
|
||||||
_filter = filter;
|
_filter = filter;
|
||||||
|
|
||||||
|
var initialValueTask = Task.Run(async () => await _filter(from.Value));
|
||||||
|
initialValueTask.Wait();
|
||||||
|
if (initialValueTask.Result)
|
||||||
|
{
|
||||||
|
SetNewValueSync(from.Value);
|
||||||
|
}
|
||||||
|
// Unfortunately we can't set a default value if the parent current value can not passed by the filter.
|
||||||
|
|
||||||
AddDisposable(from.Subscribe(SetValue));
|
AddDisposable(from.Subscribe(SetValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetValue(T? next, CancellationToken cancellationToken = default)
|
private async Task SetValue(T next, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (await _filter(next))
|
if (await _filter(next))
|
||||||
{
|
{
|
||||||
await SetNewValueAsync(next, cancellationToken);
|
await SetNewValueAsync(next, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<FilterProperty<T>> CreateAsync(
|
public static async Task<FilterProperty<T>> CreateAsync(
|
||||||
Func<T?, Task<bool>> filter,
|
Func<T?, Task<bool>> filter,
|
||||||
IDeclarativeProperty<T> from,
|
IDeclarativeProperty<T> from,
|
||||||
Action<T?>? setValueHook = null)
|
Action<T>? setValueHook = null)
|
||||||
{
|
{
|
||||||
var prop = new FilterProperty<T>(filter, from, setValueHook);
|
var prop = new FilterProperty<T>(filter, from, setValueHook);
|
||||||
await prop.SetValue(from.Value);
|
await prop.SetValue(from.Value);
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ namespace DeclarativeProperty;
|
|||||||
|
|
||||||
public interface IDeclarativeProperty<T> : INotifyPropertyChanged, IDisposable, IObservable<T>
|
public interface IDeclarativeProperty<T> : INotifyPropertyChanged, IDisposable, IObservable<T>
|
||||||
{
|
{
|
||||||
T? Value { get; set; }
|
T Value { get; set; }
|
||||||
IDisposable Subscribe(Func<T?, CancellationToken, Task> onChange);
|
IDisposable Subscribe(Func<T, CancellationToken, Task> onChange);
|
||||||
void Unsubscribe(Func<T?, CancellationToken, Task> onChange);
|
void Unsubscribe(Func<T, CancellationToken, Task> onChange);
|
||||||
|
|
||||||
IDeclarativeProperty<T> RegisterTrigger(
|
IDeclarativeProperty<T> RegisterTrigger(
|
||||||
Func<IDeclarativeProperty<T>, T, IDisposable?> triggerSubscribe,
|
Func<IDeclarativeProperty<T>, T, IDisposable?> triggerSubscribe,
|
||||||
|
|||||||
@@ -6,14 +6,19 @@ public sealed class MapProperty<TFrom, TTo> : DeclarativePropertyBase<TTo>
|
|||||||
|
|
||||||
public MapProperty(
|
public MapProperty(
|
||||||
Func<TFrom, CancellationToken, Task<TTo>> mapper,
|
Func<TFrom, CancellationToken, Task<TTo>> mapper,
|
||||||
IDeclarativeProperty<TFrom?> from,
|
IDeclarativeProperty<TFrom> from,
|
||||||
Action<TTo?>? setValueHook = null) : base(setValueHook)
|
Action<TTo>? setValueHook = null) : base(default!, setValueHook)
|
||||||
{
|
{
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
|
|
||||||
|
var initialValueTask = Task.Run(async () => await _mapper(from.Value, CancellationToken.None));
|
||||||
|
initialValueTask.Wait();
|
||||||
|
SetNewValueSync(initialValueTask.Result);
|
||||||
|
|
||||||
AddDisposable(from.Subscribe(SetValue));
|
AddDisposable(from.Subscribe(SetValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetValue(TFrom? next, CancellationToken cancellationToken = default)
|
private async Task SetValue(TFrom next, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var newValue = await _mapper(next!, cancellationToken);
|
var newValue = await _mapper(next!, cancellationToken);
|
||||||
await SetNewValueAsync(newValue, cancellationToken);
|
await SetNewValueAsync(newValue, cancellationToken);
|
||||||
@@ -21,8 +26,8 @@ public sealed class MapProperty<TFrom, TTo> : DeclarativePropertyBase<TTo>
|
|||||||
|
|
||||||
public static async Task<MapProperty<TFrom, TTo>> CreateAsync(
|
public static async Task<MapProperty<TFrom, TTo>> CreateAsync(
|
||||||
Func<TFrom, CancellationToken, Task<TTo>> mapper,
|
Func<TFrom, CancellationToken, Task<TTo>> mapper,
|
||||||
IDeclarativeProperty<TFrom?> from,
|
IDeclarativeProperty<TFrom> from,
|
||||||
Action<TTo?>? setValueHook = null)
|
Action<TTo>? setValueHook = null)
|
||||||
{
|
{
|
||||||
var prop = new MapProperty<TFrom, TTo>(mapper, from, setValueHook);
|
var prop = new MapProperty<TFrom, TTo>(mapper, from, setValueHook);
|
||||||
await prop.SetValue(from.Value);
|
await prop.SetValue(from.Value);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
public class MergeProperty<T> : DeclarativePropertyBase<T>
|
public class MergeProperty<T> : DeclarativePropertyBase<T>
|
||||||
{
|
{
|
||||||
public MergeProperty(params IDeclarativeProperty<T>[] props)
|
public MergeProperty(params IDeclarativeProperty<T>[] props) : base(default!)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(props);
|
ArgumentNullException.ThrowIfNull(props);
|
||||||
|
|
||||||
@@ -12,6 +12,6 @@ public class MergeProperty<T> : DeclarativePropertyBase<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateAsync(T? newValue, CancellationToken token)
|
private async Task UpdateAsync(T newValue, CancellationToken token)
|
||||||
=> await SetNewValueAsync(newValue, token);
|
=> await SetNewValueAsync(newValue, token);
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ public sealed class SwitchProperty<TItem> : DeclarativePropertyBase<TItem>
|
|||||||
{
|
{
|
||||||
private IDisposable? _innerSubscription;
|
private IDisposable? _innerSubscription;
|
||||||
|
|
||||||
public SwitchProperty(IDeclarativeProperty<IDeclarativeProperty<TItem>?> from) : base(from.Value is null ? default : from.Value.Value)
|
public SwitchProperty(IDeclarativeProperty<IDeclarativeProperty<TItem>?> from) : base(from.Value is null ? default! : from.Value.Value)
|
||||||
{
|
{
|
||||||
AddDisposable(from.Subscribe(HandleStreamChange));
|
AddDisposable(from.Subscribe(HandleStreamChange));
|
||||||
_innerSubscription = from.Value?.Subscribe(HandleInnerValueChange);
|
_innerSubscription = from.Value?.Subscribe(HandleInnerValueChange);
|
||||||
@@ -15,10 +15,9 @@ public sealed class SwitchProperty<TItem> : DeclarativePropertyBase<TItem>
|
|||||||
_innerSubscription?.Dispose();
|
_innerSubscription?.Dispose();
|
||||||
_innerSubscription = next?.Subscribe(HandleInnerValueChange);
|
_innerSubscription = next?.Subscribe(HandleInnerValueChange);
|
||||||
|
|
||||||
|
await SetNewValueAsync(next is null ? default! : next.Value, token);
|
||||||
await SetNewValueAsync(next is null ? default : next.Value, token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleInnerValueChange(TItem? next, CancellationToken token)
|
private async Task HandleInnerValueChange(TItem next, CancellationToken token)
|
||||||
=> await SetNewValueAsync(next, token);
|
=> await SetNewValueAsync(next, token);
|
||||||
}
|
}
|
||||||
@@ -9,13 +9,13 @@ public class ThrottleProperty<T> : DeclarativePropertyBase<T>
|
|||||||
public ThrottleProperty(
|
public ThrottleProperty(
|
||||||
IDeclarativeProperty<T> from,
|
IDeclarativeProperty<T> from,
|
||||||
Func<TimeSpan> interval,
|
Func<TimeSpan> interval,
|
||||||
Action<T?>? setValueHook = null) : base(from.Value, setValueHook)
|
Action<T>? setValueHook = null) : base(from.Value, setValueHook)
|
||||||
{
|
{
|
||||||
_interval = interval;
|
_interval = interval;
|
||||||
AddDisposable(from.Subscribe(SetValue));
|
AddDisposable(from.Subscribe(SetValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetValue(T? next, CancellationToken cancellationToken = default)
|
private async Task SetValue(T next, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
internal sealed class Unsubscriber<T> : IDisposable
|
internal sealed class Unsubscriber<T> : IDisposable
|
||||||
{
|
{
|
||||||
private readonly IDeclarativeProperty<T> _owner;
|
private readonly IDeclarativeProperty<T> _owner;
|
||||||
private readonly Func<T?, CancellationToken, Task> _onChange;
|
private readonly Func<T, CancellationToken, Task> _onChange;
|
||||||
|
|
||||||
public Unsubscriber(IDeclarativeProperty<T> owner, Func<T?, CancellationToken, Task> onChange)
|
public Unsubscriber(IDeclarativeProperty<T> owner, Func<T, CancellationToken, Task> onChange)
|
||||||
{
|
{
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
_onChange = onChange;
|
_onChange = onChange;
|
||||||
|
|||||||
Reference in New Issue
Block a user