This commit is contained in:
Dan Clark
2024-11-22 18:52:25 +00:00
parent 8a2497be82
commit e809c22cb7
15 changed files with 145 additions and 31 deletions
@@ -37,6 +37,7 @@ public class AsyncHandlerInitialization<TMessage, THandler>(IServiceProvider pro
foreach (IAsyncHandler<TMessage> handler in provider.GetServices<IAsyncHandler<TMessage>>()) foreach (IAsyncHandler<TMessage> handler in provider.GetServices<IAsyncHandler<TMessage>>())
{ {
handler.Handle(args.Message, args.CancellationToken); handler.Handle(args.Message, args.CancellationToken);
args.Reply(Unit.Value);
} }
}); });
} }
@@ -34,4 +34,12 @@ public static class IMessengerExtensions
public static async Task<TResponse> SendAsync<TMessage, TResponse>(this IMessenger messenger, public static async Task<TResponse> SendAsync<TMessage, TResponse>(this IMessenger messenger,
TMessage message) where TMessage : class => TMessage message) where TMessage : class =>
await messenger.Send(new AsyncResponseEventArgs<TMessage, TResponse> { Message = message }); await messenger.Send(new AsyncResponseEventArgs<TMessage, TResponse> { Message = message });
public static async Task SendAsync<TMessage>(this IMessenger messenger)
where TMessage : class, new() =>
await messenger.Send(new AsyncResponseEventArgs<TMessage, Unit> { Message = new TMessage() });
public static async Task SendAsync<TMessage>(this IMessenger messenger,
TMessage message) where TMessage : class =>
await messenger.Send(new AsyncResponseEventArgs<TMessage, Unit> { Message = message });
} }
@@ -43,12 +43,12 @@ public static class IServiceCollectionExtensions
if (key is { Length: > 0 }) if (key is { Length: > 0 })
{ {
services.Add(new ServiceDescriptor(typeof(IAsyncHandler<TMessage>), key, typeof(THandler), lifetime)); services.Add(new ServiceDescriptor(typeof(IAsyncHandler<TMessage>), key, typeof(THandler), lifetime));
services.AddInitialization<AsyncHandlerInitialization<TMessage, IAsyncHandler<TMessage>>>(key); services.AddInitialization<AsyncHandlerKeyedInitialization<TMessage, IAsyncHandler<TMessage>>>(key);
} }
else else
{ {
services.Add(new ServiceDescriptor(typeof(IAsyncHandler<TMessage>), typeof(THandler), lifetime)); services.Add(new ServiceDescriptor(typeof(IAsyncHandler<TMessage>), typeof(THandler), lifetime));
services.AddInitialization<AsyncHandlerKeyedInitialization<TMessage, IAsyncHandler<TMessage>>>(); services.AddInitialization<AsyncHandlerInitialization<TMessage, IAsyncHandler<TMessage>>>();
} }
return services; return services;
+4 -2
View File
@@ -67,13 +67,15 @@ public partial class Observable(IServiceProvider provider,
protected override sealed void OnActivated() protected override sealed void OnActivated()
{ {
base.OnActivated(); Messenger.RegisterAll(this);
Activated(); Activated();
} }
protected override sealed void OnDeactivated() protected override sealed void OnDeactivated()
{ {
base.OnDeactivated(); Messenger.UnregisterAll(this);
Dispose();
Deactivated(); Deactivated();
} }
} }
+6 -12
View File
@@ -1,10 +1,8 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections; using System.Collections;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Disposables; using System.Reactive.Disposables;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
@@ -38,8 +36,6 @@ public abstract partial class ObservableCollection<TViewModel> :
private readonly Dictionary<string, object> trackedProperties = []; private readonly Dictionary<string, object> trackedProperties = [];
private bool cleaning;
[ObservableProperty] [ObservableProperty]
private int count; private int count;
@@ -173,7 +169,6 @@ public abstract partial class ObservableCollection<TViewModel> :
public void Clear(bool disposeItems = false) public void Clear(bool disposeItems = false)
{ {
cleaning = true;
if (disposeItems) if (disposeItems)
{ {
foreach (TViewModel item in this.ToList()) foreach (TViewModel item in this.ToList())
@@ -184,12 +179,10 @@ public abstract partial class ObservableCollection<TViewModel> :
} }
ClearItems(); ClearItems();
cleaning = false;
} }
public void Clear() public void Clear()
{ {
cleaning = true;
foreach (TViewModel item in this.ToList()) foreach (TViewModel item in this.ToList())
{ {
Disposer.Dispose(item); Disposer.Dispose(item);
@@ -197,7 +190,6 @@ public abstract partial class ObservableCollection<TViewModel> :
} }
ClearItems(); ClearItems();
cleaning = false;
} }
public void Commit() public void Commit()
@@ -222,8 +214,8 @@ public abstract partial class ObservableCollection<TViewModel> :
public virtual void Dispose() public virtual void Dispose()
{ {
GC.SuppressFinalize(this);
Disposer.Dispose(this); Disposer.Dispose(this);
GC.SuppressFinalize(this);
} }
public IEnumerator<TViewModel> GetEnumerator() => public IEnumerator<TViewModel> GetEnumerator() =>
@@ -471,7 +463,7 @@ public abstract partial class ObservableCollection<TViewModel> :
Disposer.Add(this, item); Disposer.Add(this, item);
Disposer.Add(item, Disposable.Create(() => Disposer.Add(item, Disposable.Create(() =>
{ {
if (item is IDisposable && !cleaning) if (item is IDisposable)
{ {
if (item is IList collection) if (item is IList collection)
{ {
@@ -487,13 +479,15 @@ public abstract partial class ObservableCollection<TViewModel> :
protected override sealed void OnActivated() protected override sealed void OnActivated()
{ {
base.OnActivated(); Messenger.RegisterAll(this);
Activated(); Activated();
} }
protected override sealed void OnDeactivated() protected override sealed void OnDeactivated()
{ {
base.OnDeactivated(); Messenger.UnregisterAll(this);
Dispose();
Deactivated(); Deactivated();
} }
+1 -1
View File
@@ -7,4 +7,4 @@ public record Update
public static UpdateEventArgs<TValue> As<TValue>() where TValue : new() => public static UpdateEventArgs<TValue> As<TValue>() where TValue : new() =>
new(new TValue()); new(new TValue());
} }
+1 -1
View File
@@ -13,4 +13,4 @@ public record UpdateEventArgs<TSender>
{ {
} }
} }
+10
View File
@@ -0,0 +1,10 @@
namespace Toolkit.Foundation;
public record Updated
{
public static UpdatedEventArgs<TValue> As<TValue>(TValue value) =>
new(value);
public static UpdatedEventArgs<TValue> As<TValue>() where TValue : new() =>
new(new TValue());
}
+16
View File
@@ -0,0 +1,16 @@
namespace Toolkit.Foundation;
public record UpdatedEventArgs<TSender>
{
public TSender? Sender { get; }
public UpdatedEventArgs(TSender sender)
{
Sender = sender;
}
public UpdatedEventArgs()
{
}
}
+16 -5
View File
@@ -23,21 +23,33 @@ public static class ContentTemplateBinding
{ {
if (dependencyObject is FrameworkElement content) if (dependencyObject is FrameworkElement content)
{ {
IActivation? cachedActivation = null;
void HandleLoaded(object sender, RoutedEventArgs args) void HandleLoaded(object sender, RoutedEventArgs args)
{ {
if (content.DataContext is IActivation activation) if (sender is FrameworkElement content)
{ {
content.Loaded -= HandleLoaded; content.Loaded -= HandleLoaded;
activation.IsActive = true;
if (content.DataContext is IActivation activation)
{
cachedActivation = activation;
activation.IsActive = true;
}
} }
} }
void HandleUnloaded(object sender, RoutedEventArgs args) void HandleUnloaded(object sender, RoutedEventArgs args)
{ {
if (content.DataContext is IActivation activation) if (cachedActivation is not null)
{ {
cachedActivation.IsActive = false;
}
if (sender is FrameworkElement content)
{
cachedActivation = null;
content.Unloaded -= HandleUnloaded; content.Unloaded -= HandleUnloaded;
activation.IsActive = false;
} }
} }
@@ -45,7 +57,6 @@ public static class ContentTemplateBinding
{ {
content.Loaded += HandleLoaded; content.Loaded += HandleLoaded;
content.Unloaded += HandleUnloaded; content.Unloaded += HandleUnloaded;
} }
else else
{ {
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Globalization;
namespace Toolkit.UI.WinUI;
public class StringFormatConverter :
ValueConverter<object, object>
{
public string? StringFormat { get; set; }
protected override object? ConvertTo(object value,
Type? targetType,
object? parameter,
string? language)
{
if (value is null)
{
return null!;
}
if (string.IsNullOrEmpty(StringFormat))
{
return value.ToString()!;
}
try
{
CultureInfo culture = string.IsNullOrWhiteSpace(language) ? CultureInfo.InvariantCulture : new CultureInfo(language);
return string.Format(culture, StringFormat, value);
}
catch
{
return value;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Markup;
using System;
namespace Toolkit.UI.WinUI;
public abstract class ValueConverter<TSource, TTarget> :
MarkupExtension,
IValueConverter
{
public object? Convert(object value,
Type targetType,
object parameter,
string language) =>
ConvertTo((TSource)value, targetType, parameter, language);
public object? ConvertBack(object value,
Type targetType,
object parameter,
string language) =>
ConvertBackTo((TTarget)value, targetType, parameter, language);
public TTarget? Convert(TSource value) =>
ConvertTo(value, null, null, null);
public TSource? ConvertBack(TTarget value) =>
ConvertBackTo(value, null, null, null);
protected virtual TTarget? ConvertTo(TSource value,
Type? targetType,
object? parameter,
string? language) => default;
protected virtual TSource? ConvertBackTo(TTarget value,
Type? targetType,
object? parameter,
string? language) => default;
protected override object ProvideValue() => this;
}
-5
View File
@@ -40,11 +40,6 @@ public class ContentControlHandler :
{ {
activation.IsActive = false; activation.IsActive = false;
} }
if (content is IDisposable disposable)
{
disposable.Dispose();
}
} }
} }
@@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Toolkit.Foundation; using Toolkit.Foundation;
@@ -8,7 +9,7 @@ public static class IServiceCollectionExtensions
{ {
public static IServiceCollection AddWinUI(this IServiceCollection services) public static IServiceCollection AddWinUI(this IServiceCollection services)
{ {
services.AddTransient<IDispatcher, WinUIDispatcher>(); services.AddSingleton<IDispatcher>(provider => new WinUIDispatcher(DispatcherQueue.GetForCurrentThread()));
services.AddTransient<IDispatcherTimerFactory, DispatcherTimerFactory>(); services.AddTransient<IDispatcherTimerFactory, DispatcherTimerFactory>();
services.AddSingleton<IWindowRegistry, WindowRegistry>(); services.AddSingleton<IWindowRegistry, WindowRegistry>();
+2 -2
View File
@@ -3,12 +3,12 @@ using Toolkit.Foundation;
namespace Toolkit.WinUI; namespace Toolkit.WinUI;
public class WinUIDispatcher : public class WinUIDispatcher(DispatcherQueue dispatcherQueue) :
IDispatcher IDispatcher
{ {
public Task Invoke(Action action) public Task Invoke(Action action)
{ {
DispatcherQueue.GetForCurrentThread().TryEnqueue(action.Invoke); dispatcherQueue.TryEnqueue(action.Invoke);
return Task.CompletedTask; return Task.CompletedTask;
} }
} }